Android WindowManager

Time:2022-1-2

Android WindowManager

1、 Window classification

There are three types of window:Apply windowChild windowandSystem window。 The application class window corresponds to an activity. The child window cannot exist alone and needs to be attached to a specific parent window. For example, some common dialog is a child window. The system window is a window that can only be created by declaring permissions. For example, toast and system status bar are both system windows.

Windows are hierarchical. Each window has a corresponding z-ordered. Windows with large levels will cover windows with small levels. We can use a table to visually represent:

Window Hierarchy
Apply window 1~99
Child window 1000~1999
System window 2000~2999

2、 Using WindowManager

Our operations on window are completed through WindowManager. WindowManager is an interface that inherits from the viewmanager interface with only three methods: add view, update view and delete view.

public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

To add a window through WindowManager:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    Button floatingButton = new Button(this);
    floatingButton.setText("button");
    WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            0, 0,
            PixelFormat.TRANSPARENT
    );
    //Flag set window property
    layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
    //Type set window category (level)
    layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
    layoutParams.gravity = Gravity.CENTER;
    WindowManager windowManager = getWindowManager();
    windowManager.addView(floatingButton, layoutParams); 
}

3、 WindowManager internal mechanism

In actual use, window cannot be accessed directly. Access to window must be through WindowManager. The three interface methods addview, updateviewlayout and removeview provided by WindowManager are all for view, which shows that view is the existing entity of window. The above example implements the addition of window. WindowManager is an interface, and its 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);
        }

Windowmanagerimpl does not directly implement the three major operations of window, but is entrusted toWindowManagerGlobalTake addview as an example to analyze the implementation process in windowmanagerglobal:

1. Check the validity of the parameters. If it is a child window, make appropriate adjustments

if(view == null){
   throw new IllegalArgumentException("view must not be null");
}
 
if(display == null){
   throw new IllegalArgumentException("display must not be null");
}
 
if(!(params instanceof WindowManager.LayoutParams)){
   throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
 
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if(parentWindow != null){
   parentWindow.adjustLayoutParamsForSubWindow(wparams);
}

2. Create viewrootimpl and add view to the collection

In windowmanagerglobal, the following sets are important:

aggregate Storage content
mViews View corresponding to window
mRoots Viewrootimpl corresponding to window
mParams Layout parameters corresponding to window
mDyingViews View object being deleted

During addview operation, related objects will be added to the corresponding collection:

root = new ViewRootImpl(view.getContext(),display);
view.setLayoutParams(wparams);
 
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

3. Update the interface through viewrootimpl and complete the process of adding window

We know that the drawing process of view is completed by viewrootimpl. Of course, there is no exception here. It is implemented through the setview method of viewrootimpl. Within setview, the asynchronous refresh request will be completed through requestlayout.

public void requestLayout(){
   if(!mHandingLayoutInLayoutRequest){
       checkThread();
       mLayoutRequested = true;
       scheduleTraversals();
   }
}

You can see that the scheduletraversals method is the entry for view drawing. View its implementation:

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), 
          mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mInputChannel);

The type of mwindowsession is iwindowsession, which is a binder object. The real implementation class is session, which is the location of IPC call mentioned earlier. In the session, you can add windows through windowmanagerservice:

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);
}

Finally, the addition request of window is handed over to windowmanagerservice. A separate session will be reserved for each application in windowmanagerservice.

4、 Window creation process

View is the rendering method of view in Android, but view cannot exist alone. It must be attached to the abstract concept of window. Therefore, where there is a view, there is window. Where do you have views? Where Android can provide views, there are activity, dialog and toast. In addition, there are some views implemented based on window, such as popupwindow and menu. They are also views. Where there is a view, there is a window. Therefore, activities, dialog, toast and other views correspond to a window.

1. Window creation process of activity

After understanding the concept and meaning of window, we naturally know the window creation time of activity. Window is essentially a display area. Therefore, the window creation of activity should occur in the startup process of activity. The startup process of activity is very complex. Finally, the whole startup process will be completed by performlaunchactivity() in activitythread, Within this method, the instance object of the activity will be created through the class loader, and its attach method will be called to associate a series of context environment variables that the running process depends on.

The window creation of an activity takes place in the attach method. The system will create the window object to which the activity belongs and set the callback interface for it

mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...

It can be seen that the creation of window objects is realized through the makenewwindow method of policymanager. Since the activity implements the callback interface of window, it will call back the activity method when the window receives an external state change. There are many methods in the callback interface, several of which are familiar to us, such as onattachedtowindow, ondetachedfromwindow, dispatchtouchevent, etc.

You can see that the window of activity is created through a factory method of policymanager, but in the actual call of policymanager, the real implementation of policymanager is the policy class. The implementation of makenewwindow method in policy class is as follows:

public Window  makeNewWindow(Context context){
   return new PhoneWindow(context);
}

It can be seen that the specific implementation class of window is indeed phonewindow. Now that the window has been created, let’s analyze how the activity view is attached to the window, and the activity view is provided by setcontentview, so start with setcontentview:

public void setContentView(int layoutResID){
   getWindow().setContentView(layoutResID);
   initWindowDecorActionBar();
}

It can be seen that the activity gives the specific implementation to window, and the specific implementation of window is phonewindow, so you only need to look at the relevant logic of phonewindow. Its processing steps are as follows:

image-20211117100631881

(1) . if there is no decorview, create one

Decorview is the top-level view in the activity. It is a FrameLayout. Generally speaking, it contains a title bar and a content bar, but this will change with the theme. Anyway, the content bar must exist and have a fixed ID: “Android R. ID. content “, in phonewindow, create a decorview through the generatedecor method, and initialize the layout related to the topic through generatelayout.

(2) . add the view to the mcontentparent of decorview

This step is relatively simple. You can directly add the activity view to the mcontentparent of decorview, so you can understand the origin of the setcontentview method of activity. Why not call it setview? Because the layout file of activity is only added to the mcontentparent of decorview, it is more specific and accurate to call setcontentview.

(3) . callback the oncontentchanged method of the activity to notify the activity that the view has changed

In the previous analysis, the activity implements the callback interface of window. Here, when the view of the activity has been added to the mcontentparent of decorview, the activity needs to be notified to facilitate relevant processing.

After the above three steps, decorview has been created and initialized, and the layout file of activity has been successfully added to the mcontentparent of decorview. However, decorview has not been officially added to window by WindowManager at this time. In the handleresumeactivity method of the activitythread, the onresume method of acitivy will be called first, and then the makevisible () method of acitivy will be called. It is in the makevisible method that decorview really completes the display process, and the view of the activity can be seen by the user, as follows:

void makeVisible(){
   if(!mWindowAdded){
      ViewManager wm = getWindowManager();
      wm.addView(mDecor, getWindow().getAttributes());
      mWindowAdded = true;
   }
   mDecor.setVisibility(View.VISIBLE);
}

2. Window creation process of dialog

(1) , create window

The window in dialog is also completed through the makenewwindow method of policymanager, and the created object is also phonewindow.

(2) , initialize decorview and add the view of dialog to decorview

This process is similar to that of an activity. The specified layout file is added through window:

public void setContentView(int layoutResID){
   mWindow.setContentView(layoutResID);
}

(3) Add decorview to window and display

In the show method of dialog, decorview will be added to window through WindowManager:

mWindowManager.addView(mDecor, 1);
mShowing = true;

3. Toast window creation process

Toast is different from dialog. Its working process is slightly complex. Firstly, toast is also implemented based on window. However, because toast has the function of timing cancellation, the system adopts handler. There are two types of IPC processes in toast. One is that toast accesses notificationmanagerservice, and the other is that notificationmanagerservice calls back to the TN interface in toast. Like windowmanagerservice, notificationmanagerservice is a service located in the framework layer.

Toast belongs to the system window. Its internal views can be the system default style or customized through the setview method. In any case, they all correspond to the internal member mnextview of toast. Toast provides show and cancel to show and hide toast respectively. Their internal is an IPC process. The code is as follows:

public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }
 
        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;
 
        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }
    public void cancel() {
        mTN.hide();
 
        try {
            getService().cancelToast(mContext.getPackageName(), mTN);
        } catch (RemoteException e) {
            // Empty
        }
    }

It can be seen that the display and hiding of toast need to be implemented through NMS. TN is a binder class. When NMS processes the display or hiding request of toast, it will call back the methods in TN across processes. Since TN runs in the binder thread pool, it needs to be switched to the current thread through the handler. The current thread here refers to the thread where the toast request is sent.

The code calls the enqueueToast method of NMS in the display Toast. The enqueueToast method encapsulates the Toast request into the ToastRecord object and adds it to a queue named mToastQueue. For non system applications, there are 50 ToastRecord in the mToastQueue at most, which is used to prevent DOS (Denial ToastRecord denial of service).

After the toasterrecord is added to the mtoastqueue, the NMS will display the toasts in sequence through the shownexttoastlocked method. However, the real display of the toast is not completed in the NMS, but by the callback of the toasterrecord:

void showNextToastLocked (){
   ToastRecord record = mToastQueue.get(0);
   while(record != null){
       if(DBG) 
          Slog.d(TAG,"show pkg=" + record.pkg + "callback=" + record.callback);
       try{
          record.callback.show();
          scheduleTimeoutLocked(record);
          return;
        } 
       ...
}

It can be seen from the above code that the delay of the token lock method in the remote application will be displayed when the token lock method is called. The delay of the token lock method in the remote application will depend on the length of the token, After delaying the corresponding time, NMS will hide the toast by canceltoastlocked method and remove it from mtoastqueue. At this time, if there are other toast in mtoastqueue, NMS will continue to display other toast. The hiding of toast is also completed through the callback of toast record, which is also an IPC process.

From the above analysis, we can know that NMS only manages the toast queue and its delay. The display and hiding process of toast is actually implemented through toast’s TN class. The two methods show and hide of TN class are called by NMS in a cross process manner, so they run in the binder thread pool, In order to switch the execution environment to the thread where the toast request is located, a handler is used inside them.

Toast is to be implemented in window after all, so it will eventually be attached to WindowManager. The code in TN’s handleshow is as follows:

mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWM.addView(mView, mParams);

5、 Summary

Any view is attached to a window. Window represents the concept of a window and is also an abstract concept. Window does not actually exist. It exists in the form of view. WindowManager is the outside world, that is, the entrance for us to access window. The specific implementation of window is located in windowmanagerservice. The interaction between windowmanagerservice and WindowManager is an IPC process.

image-20211117110346243
Reference from:http://blog.csdn.net/yhaolpz https://blog.csdn.net/yhaolpz/article/details/68936932