Context Obtains system services
Before we talk about WMS, let’s look at the core principles of Context. getSystemService to find the WindowManager implementation class:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
Copy the code
private static final HashMap<String, ServiceFetcher<? >> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<? > > (); public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<? > fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher ! = null ? fetcher.getService(ctx) : null; }Copy the code
And you can see that actually all of the system services that we get from the Context are SYSTEM_SERVICE_FETCHERS, which is stored in the collection of services in the HashMap. This service is pre-registered in a static code domain.
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
Copy the code
You can see that the Windows Manager interface is actually implemented by Windows ManagerImpl at this point.
Here is a UML class diagram for Windows Manager.
We can see from this UML diagram that everything is actually delegated to Windows ManagerGlobal. So we just need to look at what’s going on in Windows ManagerGlobal.
So we’re looking for the Windows Manager addView method, which is essentially the Windows ManagerGlobal addView method.
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; if (parentWindow ! = null) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } else { // If there's no parent, then hardware acceleration for this view is // set from the application's hardware acceleration setting. final Context context = view.getContext(); if (context ! = null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) ! = 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } ViewRootImpl root; View panelParentView = null; synchronized (mLock) { // Start watching for system property changes. ... int index = findViewLocked(view, false); if (index >= 0) { if (mDyingViews.contains(view)) { // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } // If this is a panel window, then find the window it is being // attached to for future reference. if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } } root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; }}}Copy the code
So when you see a new addView here, it’s going to find out if there’s a parent Window. If no, go ahead and check whether the type of the new form is a subwindow type. If yes, the Binder objects passed in and the Binder objects stored in the cache do not have corresponding Windows. Some as the new window of the complex window.
Finally, you can see the familiar class ViewRootImpl. This class is the root of all View drawing, and we’ll talk about it later in the View drawing process. Finally, setView of ViewRootImpl will be called to further communicate with the system application end.
Here involves several interesting macros, such as the WindowManager. LayoutParams. FIRST_SUB_WINDOW. They represent the current level of the Window.
The hierarchy of the Window
The hierarchy of Windows can be roughly divided into three categories: System Window, Application Window and Sub Window.
Application Window
Application has several macros:
type | describe |
---|---|
FIRST_APPLICATION_WINDOW = 1 | Application window initial value |
TYPE_BASE_APPLICATION = 1 | Apply the initial value of the window type against which all other Windows are benchmarking |
TYPE_APPLICATION = 2 | Common application window type |
TYPE_APPLICATION_STARTING = 3 | The startup window type of the application, which is not application-dominated, is destroyed when the first application process is created |
TYPE_DRAWN_APPLICATION = 4 | WindowManager waits for the window type to be drawn before the application is displayed, usually in multi-user use |
LAST_APPLICATION_WINDOW = 99 | Maximum application window type |
So at this point we can see that the application window ranges from 1 to 99.
Sub Window
type | describe |
---|---|
FIRST_SUB_WINDOW = 1000 | Child window initial value |
TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW | Application’s Panel window, displayed in the parent window |
TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1 | Multimedia content child window, below the parent window |
TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2 | Also a panel child window on top of all Type_Application_panels |
TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3 | Dialog window pops |
TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4 | Overlay for the multimedia content window |
TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5 | Window on a child Panel |
LAST_SUB_WINDOW = 1999 | Maximum number of subwindow types |
Can see the child window range from 1000 to 1999
System Window
type | describe |
---|---|
FIRST_SYSTEM_WINDOW = 2000 | Initial value of the system window |
TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW | System status bar |
TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1 | Search bar window |
TYPE_PHONE = FIRST_SYSTEM_WINDOW+2 | Conversation window |
TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3 | Alert window, warning when battery is low |
TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4 | Screen saver window |
TYPE_TOAST = FIRST_SYSTEM_WINDOW+5 | Toast Prompt Window |
TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6 | The system overlay window, which does not respond to click events |
TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7 | Call priority layer, which displays calls in screensaver state |
TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8 | System-level dialogs such as RecentAppDialog |
TYPE_KEYGUARD_DIALOG= FIRST_SYSTEM_WINDOW+9 | Screensaver dialog box (such as qq screensaver chat box) |
TYPE_SYSTEM_ERROR= FIRST_SYSTEM_WINDOW+10 | System error window |
TYPE_INPUT_METHOD= FIRST_SYSTEM_WINDOW+11 | Input method window |
TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12 | Dialog box on the input method window |
TYPE_WALLPAPER= FIRST_SYSTEM_WINDOW+13 | Wallpaper window |
TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14 | Slide the status bar window |
LAST_SYSTEM_WINDOW = 2999 | Maximum system window |
The most common system-level Windows are these. Can notice the system window level from 2000 to 2999.
What are these levels for? These levels are used as references and will be inserted into the display stack, with the higher the level, the closer to the user. We’ll talk about that logic later.
ViewRootImpl setView
The ViewRootImpl contains many things, including the familiar process of drawing a View and adding instances of the Window.
This article is about WMS, so we only need to look at the following core function
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; mAttachInfo.mDisplayState = mDisplay.getState(); / / registration screen transformation to monitor mDisplayManager. RegisterDisplayListener (mDisplayListener, mHandler); mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); / / click event distribution mFallbackEventHandler. SetView (view). mWindowAttributes.copyFrom(attrs); if (mWindowAttributes.packageName == null) { mWindowAttributes.packageName = mBasePackageName; } attrs = mWindowAttributes; setTag(); . // Keep track of the actual window flags supplied by the client. mClientWindowLayoutFlags = attrs.flags; setAccessibilityFocus(null, null); . . mSoftInputMode = attrs.softInputMode; mWindowAttributesChanged = true; mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED; mAttachInfo.mRootView = view; mAttachInfo.mScalingRequired = mTranslator ! = null; mAttachInfo.mApplicationScale = mTranslator == null ? F: 1.0 mTranslator. ApplicationScale; if (panelParentView ! = null) { mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken(); } mAdded = true; int res; /* = WindowManagerImpl.ADD_OKAY; */ // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) ! = 0; try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); }}... }}Copy the code
This method has two core requestLayout and addToDisplay.
- 1. RequestLayout is basically the drawing process of the View and ultimately sends the pixel data to the Surface layer.
- 2. MWindowSession. AddToDisplay Window instance is added to the WMS.
Session design idea of Windows Manager
Let’s start with the Session class:
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient
Copy the code
At this point, Session implements a Binder object for IWindowSession. Binder death listening is implemented.
So where does this Session come from? Data is actually passed through the Binder object via WMS via cross-process communication:
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
Copy the code
In this way, a Session can be sent to the client with the WMs-related environment. This approach is similar to what servicemanager does with Binder, and is in fact almost identical to what Servicemanager does with Binder.
@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) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
Copy the code
Interestingly, we can see that we need to add a form instance to the WMS. Logically, we only need to do one cross-process communication. But why do you need a Session as a staging post?
You can see that sessions actually do more than just communicate with WMS. In fact, it handles drag and drop on Windows, input methods, and more importantly, sessions face multiple services in the system, but with this encapsulation, all the application needs to face is the Sesion interface, which is truly a “Session”.
What does this design mean? In fact, it is what we often call the facade design pattern.
IWindow object
Notice that in addition to IWindowSession, when we call addWindow to addWindow to WMS, there’s actually an IWindow interface. Does this IWindow mean PhoneWindow?
I’m sorry. And it isn’t. PhoneWindow is based on only the Window interface. It is not an IBinder object. Let’s go back and look at the view wrootimpl.
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession(); mDisplay = display; mBasePackageName = context.getBasePackageName(); mThread = Thread.currentThread(); mLocation = new WindowLeaked(null); mLocation.fillInStackTrace(); mWidth = -1; mHeight = -1; mDirty = new Rect(); mTempRect = new Rect(); mVisRect = new Rect(); mWinFrame = new Rect(); mWindow = new W(this); mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; mViewVisibility = View.GONE; mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added mAdded = false; mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); . mViewConfiguration = ViewConfiguration.get(context); mDensity = context.getResources().getDisplayMetrics().densityDpi; mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; mFallbackEventHandler = new PhoneFallbackEventHandler(context); mChoreographer = Choreographer.getInstance(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); if (! sCompatibilityDone) { sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P; sCompatibilityDone = true; } loadSystemProperties(); }Copy the code
You can see that at this point, the constructor in ViewRootImpl actually generates a W inner class corresponding to the current one. This inner class:
static class W extends IWindow.Stub
Copy the code
This inner class is actually a Binder class that calls back a number of methods to operate on the current ViewRootImpl. In other words, the current View Command PL agent W is handed over to WMS to manage.
So we can conclude that IWindow is used by WMS to manipulate the View in ViewRootImpl indirectly, and IWindowSession is used by App to manipulate WMS indirectly.
WMS.addWindow
WMS’s addWindow is quite long, so I’ll break it down into three parts
Add preparation steps for a form
public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) { int[] appOp = new int[1]; int res = mPolicy.checkAddPermission(attrs, appOp); if (res ! = WindowManagerGlobal.ADD_OKAY) { return res; } boolean reportNewConfig = false; WindowState parentWindow = null; long origId; final int callingUid = Binder.getCallingUid(); final int type = attrs.type; synchronized(mWindowMap) { if (! mDisplayReady) { throw new IllegalStateException("Display has not been initialialized"); } final DisplayContent displayContent = getDisplayContentOrCreate(displayId); if (displayContent == null) { ... return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (! displayContent.hasAccess(session.mUid) && ! mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) { ... return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (mWindowMap.containsKey(client.asBinder())) { ... return WindowManagerGlobal.ADD_DUPLICATE_ADD; } if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { ParentWindow = windowForClientLocked(null, attrs.token, false); if (parentWindow == null) { ... return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { ... return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } if (type == TYPE_PRIVATE_PRESENTATION && ! displayContent.isPrivate()) { ... return WindowManagerGlobal.ADD_PERMISSION_DENIED; } AppWindowToken atoken = null; final boolean hasParent = parentWindow ! = null; / / from DisplayContent find corresponding WIndowToken WIndowToken token = DisplayContent. GetWindowToken (hasParent? parentWindow.mAttrs.token : attrs.token); final int rootType = hasParent ? parentWindow.mAttrs.type : type; . }Copy the code
We throw away most of the verification logic. In fact, the process can be summarized as follows:
- 1. Do not have relevant authority to judge
- 2. Try to get the DisplayContent corresponding to the current displayId, create if there is no DisplayContent. The logic is actually the same as what I said in my last article about creating DisplayContent
- 3. Use mWindowMap to check whether the current IWindow has been added. If yes, the Window already exists and no further addition is required
- 4. If the current window type is a child window, the token in the Windowtoken. attrs parameter is used to find what the parent window of the current window is.
- 5. If there is a parent window, obtain the WindowToken object of the parent window in the IWindow of the parent window from DisplayContent; otherwise, try to obtain the WindowToken object corresponding to the current window.
Let’s explore a few of these cores a little:
Look up the WindowState of the parent window through windowForClientLocked
final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) { WindowState win = mWindowMap.get(client); if (localLOGV) Slog.v(TAG_WM, "Looking up client " + client + ": " + win); if (win == null) { if (throwOnError) { throw new IllegalArgumentException( "Requested window " + client + " does not exist"); } Slog.w(TAG_WM, "Failed looking up window callers=" + Debug.getCallers(3)); return null; } if (session ! = null && win.mSession ! = session) { if (throwOnError) { throw new IllegalArgumentException("Requested window " + client + " is in session " + win.mSession + ", not " + session); } Slog.w(TAG_WM, "Failed looking up window callers=" + Debug.getCallers(3)); return null; } return win; }Copy the code
You can actually see here that you’re getting a WindowState object from mWindowMap through IWindow. Remember the important data structure I talked about last time? MWindowMap actually holds the WindowState object corresponding to IWindow in WMS. IWindow is essentially a Binder interface for WMS control view Windows PL. Therefore, we can think of WindowState as the corresponding object of the application process.
Get the corresponding WindowToken
AppWindowToken atoken = null; final boolean hasParent = parentWindow ! = null; / / from DisplayContent find corresponding WIndowToken WIndowToken token = DisplayContent. GetWindowToken (hasParent? parentWindow.mAttrs.token : attrs.token);Copy the code
We can see from this that the WindowToken is obtained by DisplayContent.
WindowToken getWindowToken(IBinder binder) {
return mTokenMap.get(binder);
}
Copy the code
This brings us to the very important data structures I mentioned in the previous two articles :mTokenMap and mWindowMap. The two are slightly different: mWindowMap uses IWindow as the key and WindowState as value. MTokenMap uses WindowState IBinder(usually IApplicationToken) as key and WindowToken as value
Remember what mTokenMap does in the Activity launch process? When AppWIndowContainer is created, AppWindowToken is also created, and the construction of AppWindowToken adds the current IBinder as the key and AppWindowToken as the value to mTokenMap.
In other words, if the system wants to find a handle to the Window in the WMS from the IWindow given by the application process, it must go through these two layers of transformation to actually find the handle.
Split the case to get the corresponding WindowToken and AppWindowToken
At this time, there are two cases, one is the existence of WindowToken, the other is the absence of WindowToken.
boolean addToastWindowRequiresToken = false; If (token == null) {// Check window parameters are valid... final IBinder binder = attrs.token ! = null ? attrs.token : client.asBinder(); final boolean isRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) ! = 0; token = new WindowToken(this, binder, type, false, displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay); } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { atoken = token.asAppWindowToken(); if (atoken == null) { return WindowManagerGlobal.ADD_NOT_APP_TOKEN; }... } else if (atoken.removed) { ... } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow ! = null) { ... } } else if (rootType == TYPE_INPUT_METHOD) { ... } else if (rootType == TYPE_VOICE_INTERACTION) { ... } else if (rootType == TYPE_WALLPAPER) { ... } else if (rootType == TYPE_DREAM) { ... } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { ... } else if (type == TYPE_TOAST) { .... } else if (type == TYPE_QS_DIALOG) { ... } else if (token.asAppWindowToken() ! = null) { attrs.token = null; token = new WindowToken(this, client.asBinder(), type, false, displayContent, session.mCanAddInternalSystemWindow); }Copy the code
When we get Windows Token through mTokenMap, there are roughly four cases. WindowToken will try to get the parent window corresponding Token, use the WindowManager. Can’t find the WindowToken LayoutParams. Generally what we find has the father’s Windows Token.
- 1. Irrelevant applications cannot find WindowToken
- 2. The application cannot find WindowToken.
- 3. Irrelevant applications find WindowToken
- 4. Find WindowToken for the application
The first two cases
In fact, in the first two cases, once it is found that WindowToken cannot be found, if the current window is related to the application, it must be an error. Such as Toast, input method, application window and so on.
So starting with Android 8.0, when we want to display Toast, we add the Context passed in as an Application instead of an Activity, If the WindowToken corresponding to IApplicationToken cannot be found in mTokenMap, an error will be reported. Instead, you need to get the Activity’s current Context.
In the above case, the start window is applied and the Activity is not started. So it’s impossible to get checked, so no exception is thrown. It will create a Windows Token itself.
The last two kinds of analysis
When a WindowToken is found, the Activity is automatically added to mTokenMap after AppWindowToken is initialized. Now it’s a little bit more complicated.
When it is a child window, it determines whether the current WindowToken is AppWindowToken. If it is not, or is removed, an error is reported.
If it is wallpaper, input method, system popup, toast and other window modes, the modes of child window and parent window must be the same.
If the AppWindowToken is not empty, a New WindowToken will be generated because it was generated when New and has not been removed.
Why generate a new windowToken? As you can read from my previous article, as long as every call to the constructor will add the current WindowToken to mTokenMap, it is actually worrying about the rebinding of the corresponding AppWindowToken.
Add a WindowState instance to the data structure
But remember, we also need to store the associated data structures globally at this point.
final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], seq, attrs, viewVisibility, session.mUid, session.mCanAddInternalSystemWindow); if (win.mDeathRecipient == null) { ... return WindowManagerGlobal.ADD_APP_EXITING; } if (win.getDisplayContent() == null) { ... return WindowManagerGlobal.ADD_INVALID_DISPLAY; } final boolean hasStatusBarServicePermission = mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE) == PackageManager.PERMISSION_GRANTED; mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); res = mPolicy.prepareAddWindowLw(win, attrs); if (res ! = WindowManagerGlobal.ADD_OKAY) { return res; } // From now on, no exceptions or errors allowed! res = WindowManagerGlobal.ADD_OKAY; if (mCurrentFocus == null) { mWinAddedSinceNullFocus.add(win); } if (excludeWindowTypeFromTapOutTask(type)) { displayContent.mTapExcludedWindows.add(win); } origId = Binder.clearCallingIdentity(); win.attach(); Mwindowmap. put(client.asbinder (), win); // Use IWindow as key and WindowState as value. win.initAppOpsState(); . win.mToken.addWindow(win);Copy the code
Because a new WindowToken is entirely possible, a new WindowState is simply created. The Windowstate.attach method is called
void attach() {
mSession.windowAddedLocked(mAttrs.packageName);
}
Copy the code
This method is pretty important, Session does an add lock.
void windowAddedLocked(String packageName) { mPackageName = packageName; mRelayoutTag = "relayoutWindow: " + mPackageName; if (mSurfaceSession == null) { if (WindowManagerService.localLOGV) Slog.v( TAG_WM, "First window added to " + this + ", creating SurfaceSession"); mSurfaceSession = new SurfaceSession(); if (SHOW_TRANSACTIONS) Slog.i( TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession); mService.mSessions.add(this); if (mLastReportedAnimatorScale ! = mService.getCurrentAnimatorScale()) { mService.dispatchNewAnimatorScaleLocked(this); } } mNumWindow++; }Copy the code
What’s the job at this point? Contact context, when we add a PhoneWindow, we add a view wrotimpl, which adds a Session. A new interface has been created, the container object has been created, but the underlying object has not been created.
The naming logic is very similar to Session. Session is the Session object that WMS gives to the application App, while SurfaceSession is the SurfaceFlinger that needs to draw the content object for each WIndow.
The SurfaceSession and SurfaceControl are both important objects that connect to SurfaceFlinger.
Finally, add it to mWindowMap. And add WindowState to the WindowToken, so that each WindowToken assigns state information. Let’s explore the addWindow method a little bit.
WindowState Adds the Window policy
Ever consider a WindowManager. LayoutParams token from where?
When we do not specify the type of the current window, TYPE_APPLICATION = 2 is automatically set and the token will be the original AppWindowToken. When we in addView was introduced into the father window, will be through adjustLayoutParamsForSubWindow first not set the value of the application, but get the father window token:
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) { CharSequence curTitle = wp.getTitle(); if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { if (wp.token == null) { View decor = peekDecorView(); if (decor ! = null) { wp.token = decor.getWindowToken(); }}... } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW && wp.type <= WindowManager. LayoutParams. LAST_SYSTEM_WINDOW) {/ / set the title} else {the if (wp) token = = null) {wp. Token = mContainer = = null ? mAppToken : mContainer.mAppToken; } // set title} if (wp.packagename == null) {wp.packagename = McOntext.getpackagename (); } if (mHardwareAccelerated || (mWindowAttributes.flags & FLAG_HARDWARE_ACCELERATED) ! = 0) { wp.flags |= FLAG_HARDWARE_ACCELERATED; }}Copy the code
Can see right now will be initialized WindowManager. LayoutParams Token. The AppWindowToken is initialized one step earlier in the Activity startup process.
Before we talk about WindowState’s strategy for adding Windows, let’s take a look at the Constructor of WindowState.
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a, int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow, PowerManagerWrapper powerManagerWrapper) { super(service); . try { c.asBinder().linkToDeath(deathRecipient, 0); } catch (RemoteException e) { ... return; } mDeathRecipient = deathRecipient; if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.getWindowLayerLw(parentWindow) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type); mIsChildWindow = true; parentWindow.addChild(this, sWindowSubLayerComparator); mLayoutAttached = mAttrs.type ! = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD || parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER; } else { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.getWindowLayerLw(this) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = 0; mIsChildWindow = false; mLayoutAttached = false; mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; } mIsFloatingLayer = mIsImWindow || mIsWallpaper; if (mAppToken ! = null && mAppToken.mShowForAllUsers) { // Windows for apps that can show for all users should also show when the device is // locked. mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED; }... }Copy the code
Let’s focus on the initialization of mBaseLayer and mSubLayer. We can see if the type of the WindowState gets when we initialize it is a child window or not.
At this point we divide the problem into two cases:
1. It is a child window
When we find the child window of the current window, it will be divided into the following two levels as the reference value. Get the hierarchy type that is currently passed in:
MBaselayer = parent Window type * 10000 + 1000; MSubLayer = hierarchy type of the child Window itself (see table above for Window hierarchy)
private static final Comparator<WindowState> sWindowSubLayerComparator =
new Comparator<WindowState>() {
@Override
public int compare(WindowState w1, WindowState w2) {
final int layer1 = w1.mSubLayer;
final int layer2 = w2.mSubLayer;
if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
return -1;
}
return 1;
};
};
Copy the code
At this point, it is directly added to parentWindow. It keeps comparing the current mSubLayer until it finds the first insert.
2. Not a child window
It also calculates where the current window should be inserted based on the level currently passed in. The formula is as follows:
MBaselayer = current Window level type(see table above for Window level) * 10000 + 1000; mSubLayer = 0;
This will be further tweaked later via addWindow in WindowState.
private final Comparator<WindowState> mWindowComparator =
(WindowState newWindow, WindowState existingWindow) -> {
final WindowToken token = WindowToken.this;
if (newWindow.mToken != token) {
throw new IllegalArgumentException("newWindow=" + newWindow
+ " is not a child of token=" + token);
}
if (existingWindow.mToken != token) {
throw new IllegalArgumentException("existingWindow=" + existingWindow
+ " is not a child of token=" + token);
}
return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
};
protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
WindowState existingWindow) {
// New window is considered greater if it has a higher or equal base layer.
return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
}
void addWindow(final WindowState win) {
if (DEBUG_FOCUS) Slog.d(TAG_WM,
"addWindow: win=" + win + " Callers=" + Debug.getCallers(5));
if (win.isChildWindow()) {
// Child windows are added to their parent windows.
return;
}
if (!mChildren.contains(win)) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
addChild(win, mWindowComparator);
mService.mWindowsChanged = true;
// TODO: Should we also be setting layout needed here and other places?
}
}
Copy the code
This section is a continuation of the above adjustment of non-child window logic, can be easily seen, in fact, at this time to constantly compare the mBaseLayer until it finds a greater than or equal level to add to the above.
Summary of level preliminary calculation
Remember when we split the entire Collection of WindowContainers into several layers in DisplayContent? Stack area, Statusbar area, wallpaper area, input method area.
Every time we create a WindowToken, it will automatically bind to the end of the corresponding field based on the window type. At this point, when we addWindow to add WindowState, we will look up the level in the WindowToken according to this handle and insert it into the corresponding level.
To sum it up with a picture:
The level is evaluated the second time
After the above region division, the Window is roughly divided into several regions, and has a rough order, but in fact, we only dealt with the Window roughly. In fact, it is not easy to set up in the App, which is not the case when we usually use it.
Another special case is that when we try to perform a Window animation, we rarely encounter anything blocking the Activity’s Window animation. In fact, it also benefited from the second adjustment.
if (type == TYPE_INPUT_METHOD) { win.mGivenInsetsPending = true; setInputMethodWindowLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { displayContent.computeImeTarget(true /* updateImeTarget */); imMayMove = false; } else { if (type == TYPE_WALLPAPER) { displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) ! = 0) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; }}... if (imMayMove) { displayContent.computeImeTarget(true /* updateImeTarget */); } // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. win.getParent().assignChildLayers(); .Copy the code
At this point, the hierarchy is reordered from the top of DisplayContent down. You can see that the core methods are computeImeTarget and assignChildLayers.
computeImeTarget
WindowState computeImeTarget(boolean updateImeTarget) { if (mService.mInputMethodWindow == null) { if (updateImeTarget) { setInputMethodTarget(null, mService.mInputMethodTargetWaitingAnim); } return null; } final WindowState curTarget = mService.mInputMethodTarget; if (! canUpdateImeTarget()) { return curTarget; } mUpdateImeTarget = updateImeTarget; WindowState target = getWindow(mComputeImeTargetPredicate); if (target ! = null && target.mAttrs.type == TYPE_APPLICATION_STARTING) { final AppWindowToken token = target.mAppToken; if (token ! = null) { final WindowState betterTarget = token.getImeTargetBelowWindow(target); if (betterTarget ! = null) { target = betterTarget; } } } if (curTarget ! = null && curTarget.isDisplayedLw() && curTarget.isClosing() && (target == null || target.isActivityTypeHome())) { return curTarget; } if (target == null) { if (updateImeTarget) { setInputMethodTarget(null, mService.mInputMethodTargetWaitingAnim); } return null; } if (updateImeTarget) { AppWindowToken token = curTarget == null ? null : curTarget.mAppToken; if (token ! = null) { WindowState highestTarget = null; if (token.isSelfAnimating()) { highestTarget = token.getHighestAnimLayerWindow(curTarget); } if (highestTarget ! = null) { final AppTransition appTransition = mService.mAppTransition; if (appTransition.isTransitionSet()) { setInputMethodTarget(highestTarget, true); return highestTarget; } else if (highestTarget.mWinAnimator.isAnimationSet() && highestTarget.mWinAnimator.mAnimLayer > target.mWinAnimator.mAnimLayer) { setInputMethodTarget(highestTarget, true); return highestTarget; } } } setInputMethodTarget(target, false); } return target; }Copy the code
There are two objects that need to be distinguished: curTarget and Target.
- CurTarget is the mInputMethodTarget from WMS. This means that this is the WMS predetermined input method window hierarchy. This represents the current input window.
- Target is the WIndow from DisplayContent that searches for its child to the top of the WIndow that can be called the input method WIndow. This represents the next highest level (visible) input window. According to the previous articles, we can infer that at this point is to look for is added to the highest NonMagnifiableWindowContainers pop-up DisplayContent level. That’s the next window that’s going to pop up
- 1. If the WMS is no IME (input method) in the Window, at this point, there is no generated at the top of the Window, is directly obtained mInputMethodTargetWaitingAnim (because right now you need to do to input Window animation) as a new input method of pop-ups, does not need to be adjusted.
- 2. Find the top-level sub-window of DisplayContent that can become the popover level of input method through getWindow. Namely NonMagnifiableWindowContainers.
- 3. If the current popup window can be used as input method, it is the startup window type, because the startup window itself is very special, similar to the role of a transfer station. It will automatically find the window of the lower layer and judge whether it can be used as a popover.
- 4. If the current input popup window is not empty, the current process still exists, and the next window to start is Home. The input method popup window of the current process is directly returned. Avoid screen flickering. And that’s why, in our own application, when we launch the input method popover, and we hit back to the keyboard to go back to Home, sometimes the input method stays on Home.
- 5. If the popup window of the next target input method is empty, the previous one is displayed.
- 6. Input method animation playback, will be based on the method parameter updateImeTarget flag bit is open, to determine whether to process the popover animation. Because you need to animate, you need to find the highest level (visible) window under the current input method popover.
This method calls another core method here and there, setInputMethodTarget, to set the current input method popover target.
setInputMethodTarget
private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
if (target == mService.mInputMethodTarget
&& mService.mInputMethodTargetWaitingAnim == targetWaitingAnim) {
return;
}
mService.mInputMethodTarget = target;
mService.mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
}
Copy the code
You can see that in addition to assignment, you do a method very similar to the one I mentioned above assignWindowLayers.
assignWindowLayers
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
assignChildLayers(getPendingTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
scheduleAnimation();
}
Copy the code
As you can see, the assignChildLayers method is called and the window animation is performed.
assignChildLayers
void assignChildLayers(Transaction t) { int layer = 0; // We use two passes as a way to promote children which // need Z-boosting to the end of the list. for (int j = 0; j < mChildren.size(); ++j) { final WindowContainer wc = mChildren.get(j); wc.assignChildLayers(t); if (! wc.needsZBoost()) { wc.assignLayer(t, layer++); } } for (int j = 0; j < mChildren.size(); ++j) { final WindowContainer wc = mChildren.get(j); if (wc.needsZBoost()) { wc.assignLayer(t, layer++); } } } void assignChildLayers() { assignChildLayers(getPendingTransaction()); scheduleAnimation(); }Copy the code
What’s going on here? It is actually quite clever to first make an adjustment to the entire WindowContainer child form and then add the window with the needsZBoost flag bit open.
This separates the layers that need to be animated from the normal layers. Ensure that the animation window must be above the ordinary window.
Add window hierarchy adjustment summary
From this we can see that The window hierarchy management in Android 9.0 is a complete improvement over the previous window hierarchy adjustment in Android4.4.
Windows management in Android 4.4 manages Windows through complex loops that won’t be analyzed here. In Android 9.0, the window hierarchy is roughly divided into several areas, and then the cycle management of each area, and finally adjust the animation window. The obvious part of this is abstracting WIndowContainer to improve extensibility and reduce the number of loops.
The general logic to add to this window is pretty much figured out, but there’s more to it.
Let’s continue to talk about WMS exposed three interfaces for application, the remaining two interface, updateViewLayout, removeView. And how Window calculates the edge of Window.
updateViewLayout
You saw from the last article that ViewManager has another very important method called updateViewLayout. Let’s go straight to Windows ManagerGlobal and see what the real implementation class does:
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;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
Copy the code
Can see its core is very simple, is to get Windowmanager. LayoutParams ViewRootImpl needs to be updated, the final call setLayoutParams set new LayoutParams into ViewRootImpl, Finally, do an update with requestLayout.
Since this is based on View update PL to update all views on a logical screen at once, there are not many places to use it.
removeView
RemoveView is the last interface to the ViewManager. This interface is used a lot. Let’s move on to the launch window. Since we have a launch window display before launching our own real window, we must remove the launch window when the Activity is ready for the next step. Let’s see where it was removed.
Let’s focus on: resumeTopActivityInnerLocked method. This method is the only one that really starts cross-process communication and is ready to start the application’s Activity.
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { if (! mService.mBooting && ! mService.mBooted) { // Not ready yet! return false; } // Find the next top-most activity to resume in this stack that is not finishing and is // focusable. If it is not focusable, we will fall into the case below to resume the // top activity in the next focusable task. final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); final boolean hasRunningActivity = next ! = null; // TODO: Maybe this entire condition can get removed? if (hasRunningActivity && ! isAttached()) { return false; } mStackSupervisor.cancelInitializingActivities(); . return true; }Copy the code
Destruction of launch window is through ActivityStackSupervisor cancelInitializingActivities.
void cancelInitializingActivities() { for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx); for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = display.getChildAt(stackNdx); stack.cancelInitializingActivities(); }}}Copy the code
You can see that all the startup Windows in ActivityStack in ActivityDisplay will be retrieved and destroyed.
void cancelInitializingActivities() { final ActivityRecord topActivity = topRunningActivityLocked(); boolean aboveTop = true; // We don't want to clear starting window for activities that aren't behind fullscreen // activities as we need to display their starting window until they are done initializing. boolean behindFullscreenActivity = false; if (! shouldBeVisible(null)) { // The stack is not visible, so no activity in it should be displaying a starting // window. Mark all activities below top and behind fullscreen. aboveTop = false; behindFullscreenActivity = true; } for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); if (aboveTop) { if (r == topActivity) { aboveTop = false; } behindFullscreenActivity |= r.fullscreen; continue; } r.removeOrphanedStartingWindow(behindFullscreenActivity); behindFullscreenActivity |= r.fullscreen; }}}Copy the code
AboveTop is true by default. In other words, get the launch Windows of all activities from the Task history stack and kindly destroy them. If the current Activity to destroy the launch window is the same object as the Activity to be launched, there is no need to destroy it again.
void removeOrphanedStartingWindow(boolean behindFullscreenActivity) { if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) { if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this); mStartingWindowState = STARTING_WINDOW_REMOVED; mWindowContainerController.removeStartingWindow(); }}Copy the code
The behindFullscreenActivity flag indicates whether the destruction is actually performed to start the form. If an Activity is in full-screen mode, it will be destroyed.
AppWindowContainerController.removeStartingWindow
public void removeStartingWindow() { synchronized (mWindowMap) { final StartingSurface surface; . // Use the same thread to remove the window as we used to add it, as otherwise we end up // with things in the view hierarchy being called from different threads. mService.mAnimationHandler.post(() -> { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface); try { surface.remove(); } catch (Exception e) { Slog.w(TAG_WM, "Exception when removing starting window", e); }}); }}Copy the code
You can see that the operation is similar to addStartingWindow, which is the same as throwing the operation to WMS’s animation Handler mAnimationHandler, which removes the StartingSurface.
The StartingSurface, as we saw in the last article, is actually a SplashScreenSurface object.
public void remove() {
final WindowManager wm = mView.getContext().getSystemService(WindowManager.class);
wm.removeView(mView);
}
Copy the code
You can see that the launch form is now destroyed through WindowManagerService.
WindowManagerGlobal removeView
private int findViewLocked(View view, boolean required) { final int index = mViews.indexOf(view); if (required && index < 0) { throw new IllegalArgumentException("View=" + view + " not attached to window manager"); } return index; } public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); }}Copy the code
When removeView is removed, we need to note that the view is empty and there is an exception. If the view we want to destroy does not match the one found in mViews and the one found in mRoot, an error will be reported. Both objects are added simultaneously to mRoots and mView in addView.
In other words, we don’t use this method to destroy the View in the ViewGroup. This method destroys the root View of the entire form.
removeViewLocked
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()); } } boolean deferred = root.die(immediate); if (view ! = null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); }}}Copy the code
You can see that in the act of clearing, the corresponding View wrootimpl is first obtained, and the input method is destroyed by windowDismissed. Do a related destruction with ViewRootImpl, then delete the parent View specified by its View with assignParent, or add the View object to the dying View collection if it is not destroyed immediately, and then delete the View after all cleanup operations have been completed.
ViewRootImpl die
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) { doDie(); return false; } if (! mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(mTag, "Attempting to destroy the window while drawing! \n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; } void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { 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) { mWindowSession.finishDrawing(mWindow); } } catch (RemoteException e) { } } mSurface.release(); } } mAdded = false; } WindowManagerGlobal.getInstance().doRemoveView(this); }Copy the code
As you can see, doDie’s methods are executed directly if you need to understand destruction, otherwise doDie is delegated to a handler.
DoDie does a couple of things. One is to distribute the DetachedFromWindow event to the View below, then destroy all of the View’s resources in the hardwired render thread (I won’t go into that here, but I’ll cover that in a later article), release the drawing object Surface, If the current View is visible, the Session communicates with the WMS to close the drawing, and finally calls Windows ManagerGlobal’s doRemoveView.
For the time being, we are only concerned with two methods finishDrawing and doRemoveView. Let’s look at what’s inside one by one.
WMS finishDrawingWindow
void finishDrawingWindow(Session session, IWindow client) { final long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "finishDrawingWindow: " + win + " mDrawState=" + (win ! = null ? win.mWinAnimator.drawStateToString() : "null")); if (win ! = null && win.mWinAnimator.finishDrawingLocked()) { if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) ! = 0) { win.getDisplayContent().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } win.setDisplayLayoutNeeded(); mWindowPlacerLocked.requestTraversal(); } } } finally { Binder.restoreCallingIdentity(origId); }}Copy the code
Find the WindowState corresponding to IWindow and set the DisplayContent flag bit in the corresponding WindowState to true. And re-measure the edge of the form. Will a little later in-depth WindowPlacerLocked. RequestTraversal does something.
WindowManagerGlobal doRemoveView
void doRemoveView(ViewRootImpl root) { synchronized (mLock) { final int index = mRoots.indexOf(root); if (index >= 0) { mRoots.remove(index); mParams.remove(index); final View view = mViews.remove(index); mDyingViews.remove(view); } } if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { doTrimForeground(); } } private void doTrimForeground() { boolean hasVisibleWindows = false; synchronized (mLock) { for (int i = mRoots.size() - 1; i >= 0; --i) { final ViewRootImpl root = mRoots.get(i); if (root.mView ! = null && root.getHostVisibility() == View.VISIBLE && root.mAttachInfo.mThreadedRenderer ! = null) { hasVisibleWindows = true; } else { root.destroyHardwareResources(); } } } if (! hasVisibleWindows) { ThreadedRenderer.trimMemory( ComponentCallbacks2.TRIM_MEMORY_COMPLETE); }}Copy the code
As you can see, doDie frees up the necessary resources, such as the render thread and Surface, when hardware render starts. And recalculates form edges. Finally, remove all objects in mRoots and mDyingView from Windows ManagerGlobal. DoTrimForeground is the source that clears the rendering threads that correspond to views in invisible Windows.
summary
UpdateViewLayout is essentially setting up WindowParams, remeasuring and redrawing the view in the entire Window. RemoveView does the following:
- 1. Close the keyboard
- 2.ViewRootImpl calls the die method to clean up the view resource in the hardware-accelerated rendering thread and resize the Window.
- 3. Clear the remaining objects in Windows ManagerGlobal.