Underlying principle of adding a View to a Window in Android
1. WIndow and windowManagerWindow are abstract classes. The specific implementation is PhoneWindow. It is very easy to create a window. You only need to create a windowManager. The specific implementation of window is in windowManagerService, the interaction between windowManager and windowManagerService is an IPC process. The following example uses windowManager:
mFloatingButton = new Button(this); mFloatingButton.setText( "window"); mLayoutParams = new WindowManager.LayoutParams( LayoutParams. WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat. TRANSPARENT); mLayoutParams. flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams. FLAG_NOT_FOCUSABLE | LayoutParams. FLAG_SHOW_WHEN_LOCKED; mLayoutParams. type = LayoutParams. TYPE_SYSTEM_ERROR; mLayoutParams. gravity = Gravity. LEFT | Gravity. TOP; mLayoutParams. x = 100; mLayoutParams. y = 300; mFloatingButton.setOnTouchListener( this); mWindowManager.addView( mFloatingButton, mLayoutParams);
Flags and type attributes are very important. The following describes some attributes. First, flags: FLAG_NOT_TOUCH_MODAL indicates that no focus is required or various inputs are required, the final event is directly transmitted to the window with focus on the lower layer. FLAG_NOT_FOCUSABLE: Click the event to pass to the underlying window in the area outside the window. The current region is processed by yourself. This is usually set and important.
FLAG_SHOW_WHEN_LOCKED: Enables the window to be displayed on the screen lock page. Next, let's take a look at the parameter type: window has three types: application window, subwindow, and System window. The application class corresponds to an Activity. The Child Window cannot exist independently and must be attached to the parent Window, for example, a commonly used Dialog. The system Window is a window that needs to be declared and created again, such as toast. Window has the z-ordered attribute. The higher the level, the higher the level. Apply window Level 1-99, sub-window1000-1999, System 2000-2999. This level corresponds to the type parameter of windowManager. At the system level, two common types are TYPE_SYSTEM_OVERLAY or TYPE_SYSTEM_ERROR. For example, to use TYPE_SYSTEM_ERROR, you only need to use mLayoutParams. type = LayoutParams. TYPE_SYSTEM_ERROR. Add Permissions . With the basic understanding of window, let's take a look at how it loads the View at the underlying layer. 2. Create a window. In fact, the creation of Window is similar to the source code analysis of LayoutInflater, a blog I wrote earlier. Window is created in the attach method created by the Activity, through the makeNewWindow method of PolicyManager. The Callback interface of Window is implemented in the Activity, so the Activity method is called back when the window status changes. Such as onAttachedToWindow. The real implementation class of PolicyManager is Policy. Check its code:
public Window makeNewWindow(Context context) { return new PhoneWindow(context); }
The Window is created. Next we will analyze how the view is attached to the window. View the setContentView method of the Activity.
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
Set the content and set the ActionBar. The specific implementation of window is PhoneWindow, which is based on its setContent.
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. 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 { mLayoutInflater.inflate(layoutResID, mContentParent); } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
See it, and analyze it again. Here we will perform the following three steps: 1. If DecorView is not available, create DecorView in generateDecor () of installDecor. I have analyzed it before, and I will not analyze it this time. 2. Add the View to mContentParent in decorview. 3. The onContentChanged interface of the callback Activity. After the preceding operations, DecorView is created but has not been formally added to the Window. In ActivityResumeActivity, The onResume of the Activity is called first, and then the makeVisible of the Activity is called. The view is actually added to makeVisible. The Code is as follows:
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
Add the View to the Window using the addView method above. 3. Window operation View internal mechanism 1. Adding a window corresponds to a view and a viewrotimpl. The window and view are connected by viewrotimpl, which does not exist. The object is a view. You can only operate on Windows Manager. The implementation class of windowManager is windowManagerImpl. It does not directly implement three major operations, but is delegated to WindowManagerGlobal. The addView implementation is divided into the following steps: 1. Check whether the parameters are valid.
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); } else { // If there's no parent and we're running on L or above (or in the // system context), assume we want hardware acceleration. final Context context = view.getContext(); if (context != null && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } }
2. Create ViewRootImpl and add the View to the list.
root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams);
3. Update the interface and complete the window addition process through ViewRootImpl.
root.setView(view, wparams, panelParentView);
The above root is ViewRootImpl. In setView, requestLayout () is used to perform asynchronous refresh. See requestLayout:
public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
Next, we will use WindowSession to complete the window addition Process. WindowSession is a Binder object, the actual implementation class is Session, and window addition is an IPC call.
try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 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);}
In the Session, WindowManagerService is used to add a Window.
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outInputChannel); }
In WindowManagerService, a separate session is reserved for each application.
2. Delete windows. Check the removeView of WindowManagerGlobal:
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); } }
First, call findViewLocked to find the index for deleting the view. This process is to create an array traversal. Then call removeViewLocked for further deletion.
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); } } }
The delete operation is completed by viewRootImpl. WindowManager provides two deletion interfaces: removeViewImmediate and removeView. They indicate asynchronous deletion and synchronous deletion respectively. The specific delete operation is completed by the die of ViewRootImpl.
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(TAG, "Attempting to destroy the window while drawing!\n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; }
We can see that if removeViewImmediate is used, doDie is called immediately. If removeView is used, a message is sent using handler. Handler in ViewRootImpl processes the message and calls doDie. Focus on doDie:
void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(TAG, "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); }
There are four main tasks: 1. garbage collection, such as data clearing and callback. 2. Delete the Window through the remove Method of the Session, and finally call the removeWindow of WindowManagerService 3. Call dispathDetachedFromWindow and call onDetachedFromWindow () and ondetachedfrom1_winternal () internally (). When a view is removed, onDetachedFromWindow is called to recycle some resources. 4. Use doRemoveView to refresh data and delete relevant data, such as deleting objects in mRoot and mDyingViews.
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 (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) { doTrimForeground(); } }
3. Update window to view updateViewLayout in WindowManagerGlobal.
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); } }
Use setLayoutParams of viewRootImpl to update the layoutParams of viewRootImpl. Then scheduleTraversals re-la s the view, including measurement, layout, and re-painting. In addition, it also updates the window through WindowSession. This process is implemented by WindowManagerService. This is similar to the above and will not be repeated. The underlying source code of the Window is analyzed.