All involved source code is based on Android 10, API 29

The Window to add

ViewManager is the basic interface for managing views. Windows Manager inherits the ViewManager interface. WindowManagerImpl is an implementation class of windowmanager. The actual internal work is handled by WindowManagerGlobal.

WindowManagerGlobal creates a ViewRootImpl object and calls its setView method, which mainly contains two procedures on the diagram: requestLayout and addToDisplay. Where addToDisplay is the IPC call, through the Session final call WMS method. The code for resquestLayout is:

public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } } void scheduleTraversals() { if (! mTraversalScheduled) { mTraversalScheduled = true; MTraversalBarrier = mhandle.getLooper ().getQueue().postSyncbarrier (); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }} final class TraversalRunnable implements Runnable {@override public void run() { doTraversal(); } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; // Remove the message barrier mhandler.getLooper ().getQueue().removesyncbarrier (mTraversalBarrier); PerformTraversals (); }}Copy the code

The source code shows that performTraversals will eventually be executed to enter the View.

setContentView

Finally, PhoneWindow’s setContentView is called

public void setContentView(View view, Viewgroup.layoutparams Params) {// mContentParent is the place to put the XML view we defined if (mContentParent == null) {// Create a DecorView, Initialize mContentParent installDecor(); } else if (! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); If (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {view.setLayoutParams(params); final Scene newScene = new Scene(mContentParent, view); transitionTo(newScene); } else { mContentParent.addView(view, params); Private void installDecor() {if (mDecor == null) {// Create DecorView mDecor = generateDecor(-1); } else { mDecor.setWindow(this); } if (mContentParent == null) {mContentParent = generateLayout(mDecor); } } public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  / / to omit the return contentParent} from / / DecorView id for the com. Android. Internal. R.i, dc ontent of ViewGroup public < T extends the View > T findViewById(@IdRes int id) { return getDecorView().findViewById(id); }Copy the code

By the source code can be seen that the setContentView only create a DecorView and defines our view on the id for the com. Android. Internal. R.i, dc ontent ViewGroup, You haven’t actually added the view to the window through The WindowManager yet.

The execution time of Activity onCreate and onResume

  1. onCreateinActivityTheard#performLaunchActivityMethod of usingInstrumentation#callActivityOnCreatecallActivitytheonCreatemethods
  2. onResumeThe call andonCreateBasically the same thingperformResumeActivityIn the method, the final is also throughInstrumentationCall theonResumemethods

DecorView added when

In the handleResumeActivity method of ActivityTheard, you first call the performResumeActivity method and then add the DecorView to the Window using the addView method of WindowManager. From the process added by the Window above, we can see that the setView operation is finally completed through ViewRootImpl, and the three major processes of View and IPC communication with WMS are triggered.

Why can View#post get the width and height of the view

  • Why can’t onCreate and onResume be fetched

    The width and height of the View cannot be obtained in the onCreate or onResume methods of the Activity, but can be obtained through View#post. Note that the onCreate and onResume methods are executed before the View measurement. From the above, the execution time of onCreate and onResume and the time when the DecorView is added can be until the View measurement occurs after onCreate and onResume, so the width and height of the View cannot be obtained in these methods.

  • Why is View#post available
    Public Boolean post(Runnable Action) {AttachInfo final AttachInfo = mAttachInfo; // AttachInfo is not null and is placed in MessageQueue using Handler if (AttachInfo! = null) { return attachInfo.mHandler.post(action); } // The message is temporarily stored in HandlerActionQueue. GetRunQueue ().post(action); return true; Void dispatchAttachedToWindow(AttachInfo Info, int visibility) {// mAttachInfo = info; if (mRunQueue ! = null) {// Send the cached message to MessageQueue for execution mrUnqueue.executeActions (info.mHandler); mRunQueue = null; } } public void executeActions(Handler handler) { synchronized (this) { final HandlerAction[] actions = mActions; for (int i = 0, count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null; mCount = 0; }}Copy the code

    View#postYou can see the source code ofmAttachInfoWhen null, the message is staged and found by searching the source codemAttachInfoindispatchAttachedToWindowMethod is copied in theperformTraversalsSo when this method is called, the three processes of the View have already started. By the time Looper polls the message we post, the View’s width and height are measured.

View three major processes

The performMeasure, performLayout and performDraw methods are called respectively in the performTraversals method of ViewRootImpl

Measure

PerformMeasure directly calls View# Measure, and will finally call back the onMeasure method implemented by the specific ViewGroup. For ViewGroup, it is necessary to measure its subview first.

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) ! = GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } } protected void measureChild(View child, int parentWidthMeasureSpec, Int parentHeightMeasureSpec) {// final LayoutParams lp = child.getLayoutParams(); // According to the parent View of the MeasureSpec and View its own LayoutParam to View its own MeasureSpec final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); Child. Measure (childWidthMeasureSpec, childHeightMeasureSpec); } // View#onMeasure default implementation, The two MeasureSpec methods that have the specific View or ViewGroup responsible for overriding the // parameter are both the View's own MeasureSpec protected void calculated by the getChildMeasureSpec method above onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }Copy the code

Note: The MeasureSpec parameter in the onMeasure method is the current View’s own MeasureSpec

  • MeasureSpec

    A static inner class of the View class that uses an int of 32 to represent the View’s measurement mode and the corresponding measurement values. The higher 2 digits indicate the measurement mode, and the lower 30 indicates the specific value. The measurement modes are UNSPECIFIED,EXACTLY, and AT_MOST

  • The calculation of MeasureSpec

    It is mentioned above that the MeasureSpec of the View is calculated by getChildMeasureSpec

    / / childDimension: Public static int getChildMeasureSpec(int Spec, int padding, Int specMode = measurespec.getMode (spec); measurespec.getMode (spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); // Remove the padding int resultSize = 0; int resultMode = 0; // MATCH_PARENT is defined as -1 in viewGroup.layoutParams, WRAP_CONTENT defined as -2 switch (specMode) {// Parent has imposed an exact size on us Parent View Mode is EXACTLY case MeasureSpec.EXACTLY: if (childDimension >= 0) {resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. The same size as the parent View, resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. Parent View resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us if (childDimension >= 0) { // // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }Copy the code

Layout

PerformLayout directly calls View#layout, and finally calls the onLayout of the specific ViewGroup to perform the layout operation. After calculating the specific position of the neutron View in the layout, Then call the layout of the specific View to set the left, top, right, bottom parameters of the View.

Draw

The performDraw method will eventually call the specific View’s onDraw method to draw the View itself