Window creation process

Window creation process

Every time of disappointment and unhappiness will make you grow, it is impossible to be smooth sailing. Recently, I am also very upset. I have been working for a long time, sometimes Many of the things I m engaged in are not what I like, blindly moving bricks. As a bottom-level employee, you can only follow the general direction of the company, but I am also an individual. Individuals sometimes have to think more about themselves. Copy code

problem

As I said before, I want to tell you about the relationship between Window and View. I have overlooked it a bit. I will add it here. 1. I will raise a few questions:

  • What is the relationship between window and view?
  • How to set the size of the window, when do you need to set the size?
  • Event is the transmission of window and view, can we pass it across windows?

Real scene

In our daily development, we rarely encounter those who directly greet Window. Even if they do, Dialog and DialogFragment can meet our daily development needs, but sometimes we also need to customize. For example, the product once gave me a requirement. :

Requirements: IM chat. When a message comes, a floating window needs to be popped down from the top of the app. If the page is switched, it must be ensured that the pop-up window cannot be covered and is still floating at the top. Moreover, this The pop-up window can also realize the drag effect.

Analysis: The first idea is to add this card View to the Decorview of Activity, but when the page is switched, it will be covered, so it will be passed directly, and it can only be suspended in the APP by defining a separate window. In the Android API, windowmanager provides this capability.

I will tell you how to implement this function later, now let's get to the topic first.

First acquaintance with WindowManager

  • addView(View view, ViewGroup.LayoutParams params)

    Through this method, according to the passed View and layout parameters, a window is created. You can understand that this View is the Decorview of the Activity. Why the Activity needs the Decorview is actually for the convenience of developers to call, and there is no particularly important meaning.

  • updateViewLayout(View view, ViewGroup.LayoutParams params)

    You can change the layout parameters of the window, change the size and position

  • removeView(View view)

    Remove this window

Next, we analyze these three methods, sort out the process, look at the operation of WindowManager's implementation class WindowManagerImpl (in fact, there is no particularly good way, you can only comb the source code by yourself to see how Android is implemented, slow Slow down)

addView analysis

//is a singleton private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); //Handed over to WindowManagerGlobal to handle mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } Copy code

Next, we look at this class WindowManagerGlobal, pay attention to these members, through these members, we can achieve some coquettish operations, such as the realization of window time transfer, which will be mentioned later.

//The corresponding view inside the window private final ArrayList<View> mViews = new ArrayList<View>(); //ViewRootImpl corresponding to window private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); //Layout parameter Params corresponding to window private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); //The View corresponding to the deleted window calls the removeView method of windowmanager private final ArraySet<View> mDyingViews = new ArraySet<View>(); Copy code

Next, paste the core code of addview

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ViewRootImpl root; View panelParentView = null; //Create a ViewRootImpl root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); //Add View, ViewRootImpl and Params at once //At the same time their order is consistent mViews.add(view); mRoots.add(root); mParams.add(wparams); //do this last because it fires off messages to start doing things try { //Associate View and layout parameters through ViewRootImpl root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { //BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } } Copy code

Next, let's take a look at what this setView method does. Our core concern is how the Window is created and how the view is drawn. The following is the core code. (Actually, there are too many codes and various attributes, I don t know all about it, but I know what I m concerned about is how to create the window, how to control the size, and how to operate the drawing of the view, which can achieve my goal It's OK)

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView){ //This attrs contains the layout parameters we set, copy the layout parameters mWindowAttributes.copyFrom(attrs); //Found it, this is the entrance of view drawing, which can refresh the view display requestLayout(); //This method is to realize the creation of the window res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); } Copy code

mWindowSession is of the IWindowSession type, we need to pay attention to this IWindowSession ( www.androidos.net.cn/sourcecode, this is the online link to watch the Android source code) and found that this class is actually an AIDL, which is a one-time IPC process (I will also follow the IPC Explain, the students of the process will also explain later), then you need to close the real implementation class of the binder object, let's take a look.

First we look at the initialization of mWindowSession, step by step

mWindowSession = WindowManagerGlobal.getWindowSession(); public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); //Obtain the binder proxy object of IWindowManager, see the following method IWindowManager windowManager = getWindowManagerService(); //Call the binder method to get the binder object of IWindowSession sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } } public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { //It is found that sometimes an IPC operation is used to obtain the binder proxy object of IWindowManager //Later, when I talk about plug-inization, I will explain this ServiceManager in detail. As you can understand, it is a map for registering IBinder, and a lot of IPC implementations are based on this set. sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { if (sWindowManagerService != null) { ValueAnimator.setDurationScale( sWindowManagerService.getCurrentAnimatorScale()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowManagerService; } } Copy code

There are several IPC operations involved here, to summarize:

  • 1. obtain the WindowManagerService object through IPC (WindowManagerService implements IWindowManager.Stub, then she is a binder object of IWindowManager)
  • Calling the openSession method of WindowManagerService will create a Session object. This Session implements IWindowSession.Stub, which is very fresh. Session is also a binder. This Session is the concrete implementation binder of ViewRootImpl's mWindowSession

Then, after ViewRootImpl or the Binder of the Session, start to execute the addToDisplay method

@Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) { //Finally, it is handed over to WindowManagerService to handle the process of creating window return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel); } Copy code

Finally, there are too many WindowManagerService codes to analyze one by one. A lot of judgments have been made on the types of tokens and windows. Each window has a windowstate to save its state and various information about the window. Later, prepareAddWindowLw of WindowManagerPolicy will be called to prepare the window, this will not see the source code

Actually, Activity, dialog, and the window inside are all a virtual concept. What really works is the view and the boundary size. With the middleman of window, it is easy to understand and can also realize decoupled operations, including events distribution. It should be the last rendering, the window will still allocate the surface, or finish the rendering through the surface (I don't know this very well, and I still need to learn), not the window itself. Here is my understanding, follow-up, as I go deeper, I will do more explanations.

updateView

@Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); //also give this singleton object to operate mGlobal.updateViewLayout(view, params); } Copy code
public void updateViewLayout(View view, ViewGroup.LayoutParams params) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; //Change the layout parameters of View view.setLayoutParams(wparams); synchronized (mLock) { int index = findViewLocked(view, true); ViewRootImpl root = mRoots.get(index); mParams.remove(index); mParams.add(index, wparams); //This method calls scheduleTraversals internally, which will trigger measurement and layout again root.setLayoutParams(wparams, false); } } Copy code

removeView

Similarly, we need to look at mGlobal.removeView

public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { //In the mViews collection, find the view index to be deleted int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); //Start to execute specific deletion logic removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view "+ view + "but the ViewAncestor is attached to" + curView); } } Copy code
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } //The default immediate is false, and the delete operation is implemented asynchronously boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { //Put the View to be deleted in the mDyingViews collection mDyingViews.add(view); } } } Copy code

Next, analyze the specific deletion process

boolean die(boolean immediate) { //Make sure we do execute immediately if we are in the middle of a traversal or the damage //done by dispatchDetachedFromWindow will cause havoc on return. if (immediate && !mIsInTraversal) { //delete immediately doDie(); return false; } if (!mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(mTag, "Attempting to destroy the window while drawing!\n" + "window=" + this + ", title=" + mWindowAttributes.getTitle()); } //Asynchronously delete, send a message, switch through the handler mHandler.sendEmptyMessage(MSG_DIE); return true; } Copy code

Whether it is synchronized or switched through the handler, it will go to the doDie method

void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(mTag, "DIE in "+ this +" of "+ mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { //This method, I will talk about it later dispatchDetachedFromWindow(); } if (mAdded && !mFirst) { destroyHardwareRenderer(); if (mView != null) { int viewVisibility = mView.getVisibility(); boolean viewVisibilityChanged = mViewVisibility != viewVisibility; if (mWindowAttributesChanged || viewVisibilityChanged) { //If layout params have been changed, first give them //to the window manager to make sure it has the correct //animation info. try { if ((relayoutWindow(mWindowAttributes, viewVisibility, false) & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { //There is an IPC call, which is related to animation, not our concern mWindowSession.finishDrawing(mWindow); } } catch (RemoteException e) { } } //Resource release mSurface.release(); } } mAdded = false; } //Keep the data related to this View in mViews, mRoots, and mDyingViews, and clear them //The more important collections mentioned earlier, it seems that their life cycles are bound WindowManagerGlobal.getInstance().doRemoveView(this); } Copy code

This method is the real deletion logic

void dispatchDetachedFromWindow() { mFirstInputStage.onDetachedFromWindow(); if (mView != null && mView.mAttachInfo != null) { mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); //This method should be familiar to everyone. We often monitor this method to release our own resources mView.dispatchDetachedFromWindow(); } //Most of the following are the release of some resources and references, remove the callback mAccessibilityInteractionConnectionManager.ensureNoConnection(); mAccessibilityManager.removeAccessibilityStateChangeListener( mAccessibilityInteractionConnectionManager); mAccessibilityManager.removeHighTextContrastStateChangeListener( mHighContrastTextManager); removeSendWindowContentChangedCallback(); destroyHardwareRenderer(); setAccessibilityFocus(null, null); mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; mSurface.release(); if (mInputQueueCallback != null && mInputQueue != null) { mInputQueueCallback.onInputQueueDestroyed(mInputQueue); mInputQueue.dispose(); mInputQueueCallback = null; mInputQueue = null; } if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; } try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } //Dispose the input channel after removing the window so the Window Manager //doesn't interpret the input channel being closed as an abnormal termination. if (mInputChannel != null) { mInputChannel.dispose(); mInputChannel = null; } mDisplayManager.unregisterDisplayListener(mDisplayListener); //Remove the callback and barrier message (about this barrier message, the subsequent analysis handler will explain it to you, the purpose is to speed up the rendering of the UI//dye) unscheduleTraversals(); } Copy code

Regarding, mWindowSession.remove(mWindow) is similar to addView, and will eventually use the method removeWindow of WindowManagerService, and finally the method of removeWindowLw of WindowManagerPolicy will be called.

summary

Generally speaking, the process is basically dredged, but when it comes to WindowManagerPolicy, I can't find the code (it should be insufficient skill, I will learn later, I will continue to add it) This may involve a lower level, More details.

Everyone needs to know the following points are enough

  • The creation process of window, experienced IPC operation with WMS
  • The relationship between view, window, ViewRootImpl

Next, we return to the first few questions:

  • What is the relationship between window and view?

Android's window is a virtual concept, and the core is the display of View. It is usually understood that it can be a layer, or a display constraint and boundary, and the core rendering is still realized through surface. A window corresponds to a view tree. It is convenient for us to manage the view through the window, manage each view tree displayed on the screen, their event distribution, and their level, etc. (The priority of each window is different, priority High will be displayed at the top), and pass the token verification at the window level. It may be a bit abstract. The simple understanding is that window is a teacher of a class and manages the class, but the average grade of the class is suitable for student-related (student score/number of students).

  • How do you set the size of the window, and when do you need to set the size? (You don t need to pay special attention to this, just check the api at that time)

The window can constrain the boundary. We don t need to change it by default. The window size of the windowmanager can be changed by layout parameters. When you need full screen or other situations, you can control when you need to customize your UI, and I want to intercept the screen. All events can also be intercepted through the full screen. (If it is not full screen and touch other locations, the event will not come to your winow at all, how to intercept it).

  • Event is the transmission of window and view, can we pass it across windows?

By default, the click event will only be delivered in the current window. If you can cross the window arbitrarily, there will be a problem (open a new activity, the covered activity also has this event, is this a bug), but it is also operable , We get WindowManager (actually an instance object of WindowManagerIMpl) through api, and then get the mGlobal object through reflection, and then get the mViews collection, we can get the View root node of another window, which can be understood as DecorView, And then redistribute the event dispatchTouchEvent(ev) through this DecorView.

Core function description:

  • WindowManager:
    • It is an interface that provides three methods. The implementation class is WindowManagerImpl. Finally, it is realized through the singleton WindowManagerGlobal to perform various operations on the window.
  • ViewRootImpl
    • Responsible for the bridge connecting the view and the window.
    • Responsible with the IPC classmates of WindowManagerService.
    • Responsible for managing and drawing view views.
  • WindowManagerService
    • This is where Window is specifically created and managed