Sometimes we need to display something like a floating Window on the desktop. This effect is implemented using Window, which is an abstract class representing a Window whose concrete implementation is PhoneWindow in WindowManagerService.

A, Window

Window is an abstract class that is implemented PhoneWindow and window is an abstract concept. Each window corresponds to a DecorView and a ViewRootImpl, Window and DecorView are connected via ViewRootImpl

There are three types of Windows: application Window, child Window, and system Window.

  1. The application WindowApplication Window corresponds to an Activity
  2. The child Window: A child Window cannot stand alone and must be attached to a specific parent Window. For example, some common dialogs are child Windows.
  3. The Window system: System Window is a Window that can be created only after the permission is declared. For example, Toast and the system status bar are system Windows

A Window is hierarchically ordered. Each Window has a corresponding Z-ordered order, and those with a higher order are overwritten by those with a lower order. This is exactly the same as the CONCEPT of Z-index in Html. In the three types of Windows, the application Window level ranges from 1 to 99, the sub-window level ranges from 1000 to 1999, and the system Window level ranges from 2000 to 2999, as follows:

Window Scope of the hierarchy
The application Window 1~99
The child Window 1000 ~ 1999
The Window system 2000 ~ 2999

The scope of the hierarchy corresponds to the WindowManager. LayoutParams type parameters, if you want the Window located in the top all Windows, it can be use higher level, it is clear that the system Window hierarchy is one of the biggest, when USES the system level, need to declare permissions.

Second, the WindowManager

2.1 introduction

WindowManager is the entry point for the outside world to access Windows. WindowManager is an interface that inherits from the ViewManager interface, which has only three methods. The implementation class of WindowManager is WindowManagerImpl

public interface ViewManager{

    / / add the View

    public void addView(View view, ViewGroup.LayoutParams params);

    / / update the View

    public void updateViewLayout(View view, ViewGroup.LayoutParams params);

    / / delete the View

    public void removeView(View view);

}

Copy the code

These three methods are the main functions provided by WindowManager, namely, adding views, updating views, and deleting views.

Example of WindowManager adding a Window:

  public class MainActivity extends AppCompatActivity{



     @Override

     protected void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);



        Button floatingBtn = new Button(this);

        floatingBtn.setText("button");



        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(

            WdinowManager.LayoutParams.WRAP_CONTENT,

            WdinowManager.LayoutParams.WRAP_CONTENT,

            0.0.

            PixelFormat.TRANSPARENT

        );

        // flag Sets the Window attribute

        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        // type Sets the Window category (hierarchy)

        layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;



        layoutParams.gravity = Gravity.CENTER;



        WindowManager windowManger = getWindowManager();

        // Add a view

        windowManger.addView(floatingBtn,layoutParams);



     }

  }

Copy the code

Instead of calling the Activity’s setContentView method, the code directly adds the Window through The WindowManager, which is set to the system Window, so you should add permissions

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Copy the code

The results are shown below:

Button added to Window

The second interface is the lock screen interface, because the button is in the system Window of a larger level, so you can see the button.

2.2 Internal Mechanism of WindowManager

WindowManager inheritance:

The inheritance relationship UML for WindowManager

In practice, the Window cannot be accessed directly. Access to the Window must be through WindowManager. WindowManager provide three interface method addView updateViewLayout, removeView, are for the View, this shows that the View is the entity of the existence of the Window, the example above the Window to add, WindowManager is an interface whose real implementation is the WindowManagerImpl class:

@Override

        public void addView(View view, ViewGroup.LayoutParams params){

            mGlobal.addView(view, params, mDisplay, mParentWindow);

        }



        @Override

        public void updateViewLayout(View view, ViewGroup.LayoutParams params){

            mGlobal.updateViewLayout(view, params);

        }



        @Override

        public void removeView(View view){

            mGlobal.removeView(view, false);

        }

Copy the code

WindowManagerImpl does not directly implement Windows’ three operations, but is given to WindowManagerGlobal to handle. The following uses addView as an example to analyze the implementation process of Windows Managerglobal:

  1. Check the validity of the parameter and adjust it if it is a child Window
if(view == null) {

   throw new IllegalArgumentException("view must not be null");

}



if(display == null) {

   throw new IllegalArgumentException("display must not be null");

}



if(! (paramsinstanceof WindowManager.LayoutParams)){

   throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");

}



final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

if(parentWindow ! =null) {

   parentWindow.adjustLayoutParamsForSubWindow(wparams);

}

Copy the code
  1. Create the ViewRootImpl and add the View to the collection

There are several important collections within Windows ManagerGlobal:

private final ArrayList<View> mViews = new ArrayList<View>();

private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();

private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

private final ArraySet<View> mDyingViews = new ArraySet<View>();

Copy the code

Among them: MViews stores views corresponding to all Windows, mRoots stores ViewRootImpl corresponding to all Windows, and mParams stores layout parameters corresponding to all Windows. MDyingViews stores View objects that are being removed, or Window objects that have already been removed from the removeView method.

A collection of Store content
mViews The View corresponding to the Window
mRoots Window corresponds to view wrootimpl
mParams The layout parameter of the Window
mDyingViews View object being deleted

The addView operation adds the related object to the corresponding collection:

root = new ViewRootImpl(view.getContext(),display);

view.setLayoutParams(wparams);



mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

Copy the code
  1. Update the interface and add the Window using ViewRootImpl. When we learned how a View works, we learned that the View is drawn by ViewRootImpl, and this is no exception, This is done through the setView method of ViewRootImpl. RequestLayout is used to complete the asynchronous refresh request within the setView as follows:
public void requestLayout(a){

   if(! mHandingLayoutInLayoutRequest){

       checkThread();

       mLayoutRequested = true;

       scheduleTraversals();

   }

}

Copy the code

You can see that the scheduleTraversals method is the entry point for the View to draw. Continue to look at its implementation:

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), 

          mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mInputChannel);

Copy the code

The type of mWindowSession is IWindowSession, which is a Binder object. The real implementation class is Session, which is where the IPC calls are. The Window is added inside the Session using WindowManagerService as shown below

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams, attrs, int viewVisibility, 

                  int displayId, Rect outContentInsets, InputChannel outInputChannel)
{

   return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel);

}

Copy the code

Finally, the Window add request is handed over to WindowManagerService, which reserves a separate Session for each application within WindowManagerService. The process of adding Windows from the application layer to the Framework is described in the following figure:

Windows Manager addView process

It is easy to understand that the process of adding, deleting, and updating Windows is similar, and that they all end up being handled by an IPC process that hands over operations to WindowManagerService, a Window management service in the Framework layer.

3. WindowManagerService

WindowManagerService is a window management service located in the FrameWork layer. Its responsibility is to manage all Windows. What is the nature of a window? It’s basically a display area, which in Android is the canvas for drawing: the Surface. When a Surface displays on the screen, that’s what the user sees. WindowManagerServce Is the process by which Windows ManagerService assigns a Surface to a window. The surfaces are arranged in order on the screen by WindowManagerService. Android has a colorful interface. Therefore, the Display system of Android can be divided into three levels according to the operation type of Surface:

WindowManagerService

In general development, we operate on the UI framework layer, the operation of Windows can be done through WindowManager, and WindowManagerService as a system and service runs in a separate process, All interaction between Windows Manager and Windows ManagerService is an IPC process

The Window creation process

View is the Android way of presenting a View, but a View cannot exist on its own, it must be attached to the abstract concept of Window, so where there is a View, there is Window.

Where are Windows? Android can provide views for activities, Dialogs, and Toasts. In addition, there are also some window-based views, such as PopUpWindow (custom pop-up Window) and menus. They are also views, and wherever there is a view, there is a Window. So the Activity, Dialog, Toast, and so on all correspond to a Window. This is a common interview question: How many Windows are there in an application? The following sections examine the Window creation processes for Activity, Dialog, and Toast.

A Window is essentially a display area, so the creation of a Window for an Activity should occur during the startup of the Activity. The complex process of starting an Activity is performed by performanLaunchActivity() in ActivityThread, which creates an instance of the Activity through the class loader. And call its attach method to associate it with a series of context variables that it depends on during operation.

How is the Window instance of the Activity created?

In the Attach method, the system creates the Window object to which the Activity belongs and sets its callback interface as follows: the attach method, the internal method of the Activity, is called by the ActivityThread:

final void attach(Context context, ActivityThread aThread,

            Instrumentation instr, IBinder token, int ident,

            Application application, Intent intent, ActivityInfo info,

            CharSequence title, Activity parent, String id,

            NonConfigurationInstances lastNonConfigurationInstances,

            Configuration config, String referrer, IVoiceInteractor voiceInteractor,

            Window window, ActivityConfigCallback activityConfigCallback)
 
{

        attachBaseContext(context);



        mFragments.attachHost(null /*parent*/);



        // Instantiate the Window object, which is PhoneWindow

        mWindow = new PhoneWindow(this, window, activityConfigCallback);

        // The Activity implements window. Callback



        // Set the Window controller callback

        mWindow.setWindowControllerCallback(this);

        // Set the Window callback

        mWindow.setCallback(this);

        // Set the callback for Window disappearing

        mWindow.setOnWindowDismissedCallback(this);



        mWindow.getLayoutInflater().setPrivateFactory(this);

        if(info.softInputMode ! = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {

            mWindow.setSoftInputMode(info.softInputMode);

        }

        if(info.uiOptions ! =0) {

            mWindow.setUiOptions(info.uiOptions);

        }

        / / the UI thread

        mUiThread = Thread.currentThread();

        / / main thread

        mMainThread = aThread;

        mInstrumentation = instr;

        // The attached Window token

        mToken = token;

        mIdent = ident;

        // Application information

        mApplication = application;

        mIntent = intent;

        mReferrer = referrer;

        mComponent = intent.getComponent();

        // The information for this activity is the same as in XML

        mActivityInfo = info;

        mTitle = title;

        mParent = parent;

        mEmbeddedID = id;

        mLastNonConfigurationInstances = lastNonConfigurationInstances;

        if(voiceInteractor ! =null) {

            if(lastNonConfigurationInstances ! =null) {

                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;

            } else {

                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this.this.

                        Looper.myLooper());

            }

        }

        // The activity's Window is set to WindowManager

        mWindow.setWindowManager(

                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),

                mToken, mComponent.flattenToString(),

(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);

        if(mParent ! =null) {

            mWindow.setContainer(mParent.getWindow());

        }

        / / assignment WindowManager

        mWindowManager = mWindow.getWindowManager();

        mCurrentConfig = config;



        mWindow.setColorMode(info.colorMode);

    }

Copy the code

This method does two things: (1) Instantiate the Window object, which is an instance of PhoneWindow. (2) Set the Activity’s Window object as a Callback, which is already implemented by the Activity. (3) Assign other threads, Activity information, Window Sets WindowManager and so on

Window.Callback is already implemented by the Activity:

public class Activity extends ContextThemeWrapper

        implements LayoutInflater.Factory2.

        Window.Callback.KeyEvent.Callback.

        OnCreateContextMenuListener.ComponentCallbacks2.

        Window.OnWindowDismissedCallback.WindowControllerCallback.

        AutofillManager.AutofillClient 
{

Copy the code

Callback interface has many methods, such as onAttachedToWindow, onDetachedFromWindow, dispatchTouchEvent and so on

Callback interface method

After the Activity immplements window. Callback interface method, the Window sets the Callback to mwindow.setcallback (this), All external Callback states of the Activity’s Window are in the Activity. If you want to know the state of the Window, override the window.callback method implemented in the Activity.

How is the Activity view attached to the Window?

We know that the Activity view is provided by the setContentView, so start with Activity#setContentView:

/ * *

     * Set the activity content from a layout resource.  The resource will be

     * inflated, adding all top-level views to the activity.

     *

     * @param layoutResID Resource ID to be inflated.

     *

     * @see #setContentView(android.view.View)

     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)

* /


    public void setContentView(@LayoutRes int layoutResID) {

        // You can see that the layout file is actually set to Window

        getWindow().setContentView(layoutResID);

        initWindowDecorActionBar();

    }

Copy the code

The specific implementation of Windows is PhoneWindow, just look at the relevant logic of PhoneWindow:

.



    // This is the top-level view of the window, containing the window decor.

    // This is the Window's top View, which contains the Content layout

    private DecorView mDecor;



    // This is the view in which the window contents are placed. It is either

    // mDecor itself, or a child of mDecor where the contents go.

    // The View is the Window's Content layout, which is either Decor itself or the Content of the Decor

    ViewGroup mContentParent;



.



    @Override

    public void setContentView(int layoutResID) {

        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window

        // decor, when theme attributes and the like are crystalized. Do not check the feature

        // before this happens.



        // 1, if the Content layout is null, initialize the DecorView and generate the Content layout

        if (mContentParent == null) {

            installDecor();



        } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

            mContentParent.removeAllViews();

        }



        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,

                    getContext());

            transitionTo(newScene);

        } else {

            // 2, add our XML layout to the Content layout in the DecorView

            mLayoutInflater.inflate(layoutResID, mContentParent);

        }

        mContentParent.requestApplyInsets();

        final Callback cb = getCallback();

        if(cb ! =null && !isDestroyed()) {

            // 3, return to the Activity's onContentChanged method to inform us that the XML layout is attached to the DecorView

            cb.onContentChanged();

        }

        mContentParentExplicitlySet = true;

    }



    // Initialize the DecorView and generate the Content layout

    private void installDecor(a) {

        mForceDecorInstall = false;

        if (mDecor == null) {

            mDecor = generateDecor(-1);

            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

            mDecor.setIsRootNamespace(true);

            if(! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! =0) {

                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);

            }

        } else {

            mDecor.setWindow(this);

        }



        If the Content layout is null, it is initialized from the DecorView

        if (mContentParent == null) {

            mContentParent = generateLayout(mDecor);



            }



. Other code

        }

Copy the code

So, see the PhoneWindow source code above

  1. The PhoneWindow contains DecorView and Content layout instances
  2. SetContentView when adding the XML layout file
  3. A DecorView is the top View in your Activity. It is a FrameLayout. Normally, it contains a title bar and a interior bar, but this changes with the theme. With a fixed ID: “Android.r.D.C. Tent”. In PhoneWindow, create a DecorView with the generateDecor method and initialize the theme-related layout with generateLayout.
  4. Adding the View to the mContentParent of the DecorView is a simple step. Just add the Activity View to the mContentParent of the DecorView. The setContentView method of an Activity is called setView. Because the Activity layout file is only added to the mContentParent of the DecorView, it is more accurate to call it setContentView.
  5. The onContentChanged method that calls back to the Activity notifies the Activity that the view has changed. You need to notify the Activity that its view has been added to the mContentParent DecorView so that it can handle it.

After the above three steps, the DecorView has been created and initialized, and the Activity layout file has been successfully added to the DecorView’s mContentParent. But at this point the DecorView has not been officially added to the Window by WindowManager.

In ActivityThread’s handleResumeActivity method, Acitivy’s onResume method is first called, followed by Acitivy’s makeVisible() method, It is in the makeVisible method that the DecorView actually completes the display so that the Activity view is visible to the user, as follows:

void makeVisible(a){

   if(! mWindowAdded){

      ViewManager wm = getWindowManager();

      wm.addView(mDecor, getWindow().getAttributes());

      mWindowAdded = true;

   }

   mDecor.setVisibility(View.VISIBLE);

}

Copy the code

A Dialog Window is created in the same way as an Activity:

(1) Creating a Window Dialog is also done through the makeNewWindow method of PolicyManager and the created object is also PhoneWindow. Initialize a DecorView and add the Dialog view to the DecorView. This process is similar to an Activity in that it adds the specified layout file through the Window

public void setContentView(int layoutResID){

   mWindow.setContentView(layoutResID);

}

Copy the code

(3) Add a DecorView to the Window and display it in the Dialog show method.

mWindowManager.addView(mDecor, 1);

mShowing = true;

Copy the code

As you can see from the above three steps, the Dialog Window creation process is similar to the Activity creation process in that when the Dialog is closed, it removes the DecorView through the WindowManager. A normal Dialog must use the Activity Context, and an error is reported if the Application Context is used. This is because there is no application token, which is generally owned by the Activity. In addition, the system Window is special and does not need a token.