I. General process analysis:

1, handleResumeActivity

ActivityThread’s handleResumeActivity method is called in the process that starts the activity.

The handleResumeActivity method, shown below, does two main things;

The performResumeActivity at comment 1 is called to the onResume method in this method, so if we get the width and height of the layout neutron view before the Resume life cycle, it returns 0 because the addView process has not yet been executed;

At comment 2, the addView method is executed through WM. The decor is the DecorView we introduced in the previous section. This WM is of the ViewManager interface type, the WindowManager interface inherits from ViewManager; WindowManagerImpl implements WindowManager. This addView is actually the addView of the called WindowManagerImpl.

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); //1 if (r == null) { // We didn't actually resume the activity, so skipping any follow-up actions. return; } final Activity a = r.activity; if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; wm.addView(decor, l); / / 2}}Copy the code

2. WindowManagerImpl addView is shown below. Comment 1 shows the addView for the mGlobal call, which is of Type WindowManagerGlobal and is created in singleton mode. WindowManagerGlobal is a singleton class;

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId()); //1 } private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); //2 public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; }}Copy the code

3, WindowManagerGlobal addView: creates the ViewRootImpl object in comment 1, calls the ViewRootImpl setView method in comment 2, and the view argument is still the DecorView that contains our layout.

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; ViewRootImpl root; synchronized (mLock) { root = new ViewRootImpl(view.getContext(), display); //1 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, userId); / / 2}}}Copy the code

RequestLayout is called in the setView method of ViewRootImpl, which in turn calls scheduleTraversals, which in turn calls doduleTraversal. DoTraversal will invoke performTraversals, which should be familiar, where the view’s measured layout is plotted.

5. Formtraversals is a very long method, but it basically does three things: measurement, layout and rendering, as shown below.

Note 1 assigns the mView to host. This mView is assigned to docorView in the ViewRootImpl setView method, so this host is the decorView that contains our layout;

PerformMeasure performed at Note 2 represents the measurement process.

PerformLayout at comment 3 represents the layout process;

PerformDraw in comment 4 represents the drawing process;

private void performTraversals() { // cache mView since it is used so much below... final View host = mView; //1 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //2 performLayout(lp, mWidth, mHeight); //3 performDraw(); / / 4}Copy the code

Two, the measurement process measure

1, performMeasure

The measurement process starts with the performMeasure method, as shown below. Comment 1 indicates that if the mView is empty, it is returned directly. This mView we assigned in the setView method is actually the DecorView that contains our layout. DecorView, FrameLayout, and FrameLayout ViewGroup have no measure methods, so the measure method at comment 2 is actually the measure method of the View.

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; //1 } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); //2 } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}Copy the code

2, View’s measure method

The measure method actually does one thing: it calls onMeasure. The DecorView overrides the onMeasure method, so it’s the onMeasure method of the calling DecorView. Call super.onMeasure(widthMeasureSpec, heightMeasureSpec) in the DecorView onMeasure method.

Public final void measure(int widthMeasureSpec, int heightMeasureSpec) {public final void measure(int widthMeasureSpec, int heightMeasure) { heightMeasureSpec); ,,,,}Copy the code

3. FrameLayout onMeasure method

Note 1: First calculate the number of child views;

Comment 2 indicates that we loop through each child view and then execute measureChildWithMargins;

After measuring all the child views, set its own size in comment 3.

Comments 4 and 5 indicate that all child views with the MatchParent attribute will be re-measured. Because the MatchParent attribute is special, the size of the parent view is not known at the beginning, so it needs to be re-measured.

Continuing with comment 2, FrameLayout does not have the measureChildWithMargins method, but is defined in the parent ViewGroup, Let’s look at the measureChildWithMargins method of the ViewGroup;

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); //1 for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() ! = GONE) { measureChildWithMargins(child, widthMeasureSpec, 0,heightMeasureSpec,0); //2 } } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); //3 count = mMatchParentChildren.size(); //4 if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); Measure (childWidthMeasureSpec, childHeightMeasureSpec); / / 5}}}Copy the code

4, measureChildWithMargins method for ViewGroup

Note 1: calculate the LayoutParams layout parameters of the child view;

The parent view’s MeasureSpec and the child view’s MeasureSpec layout parameters are used to determine the child view’s MeasureSpec in comments 2 and 3.

Measure (childWidthMeasureSpec, childHeightMeasureSpec); In this case, the measure method of the View will be recursively called, and the onMeasure method will be recursively called in the measure method of the View. If the subview is of a viewgroup type, the onMeasure method of the viewgroup will be recursively called. The onMeasure method of the view will be called until the lowest level view is recursively called.

protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //1 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); //2 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); //3 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); / / 4}Copy the code

5, View onMeasure method

The setMeasuredDimension method at comment 1 sets its own size, using getDefaultSize;

Comments 3 and 4 assign mMeasuredWidth and mMeasuredHeight to the values we measured; So when the measure process is finished we can get the measurement using getMeasuredWidth and hgetMeasuredeight, for example in the onLayout method;

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); //1
}

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    、、、
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);//2
}

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth; //3
    mMeasuredHeight = measuredHeight; //4

    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
Copy the code

6. View getDefaultSize

SpecMode is obtained at comment 1;

Note 2: If we set wrap_content or match_parent to a custom view that inherits directly from the view, the effect is the same.

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec); //1
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY: //2
        result = specSize;
        break;
    }
    return result;
}
Copy the code

3, the layout process

1, performLayout method

The entry function to the layout process is performLayout, as shown below. DecorView is assigned to host at comment 1, and host’s layout is executed at comment 2, which is the layout of the decorView. DecorView, FrameLayout, and FrameLayout ViewGroup do not overwrite the Layout function, so we call the View layout, which is the same logic as measure.

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mInLayout = true;
    final View host = mView; //1
    if (host == null) {
        return;
    }
    
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); //2
    mInLayout = false;
}
Copy the code

2, View layout method

The DecorView overrides the onLayout method, Call super.onLayout(changed, left, Top, right, bottom) in the DecorView onLayout. So FrameLayout’s onLayout method;

The setFrame method in comment 1 is mainly to set the size of the current view. The specific implementation is to assign values to the upper left, the lower right and the lower left in comment 2, 3, 4 and 5 respectively. So after the layout process can be executed by getWidth and getHeight view width and height, we generally call these two methods in the onDraw method to obtain the width and height of the view;

Public void layout(int l, int t, int r, int b) {,,, Boolean changed = isLayoutModeOptical(mParent)? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); //1 onLayout(changed, l, t, r, b); SetFrame (int left, int top, int right, int bottom) {Boolean changed = false; // Invalidate our old position invalidate(sizeChanged); ,,, mLeft = left; //2 mTop = top; //3 mRight = right; //4 mBottom = bottom; // return changed; }Copy the code

3. FrameLayout onLayout method

Comment 1: onLayout calls layoutChildren;

Note 2 calculates the number of child Views; Note 3 loops through each sub-view and executes the layout method of the sub-view; So it executes to the View layout method, which in turn executes to the onLayout method; If the child view is also a viewgroup, the above operation is repeated; If the child view is a concrete view, it calls the onLayout of the view, and the onLayout of the view is empty. Sizing assignments were introduced in Step 2;

protected void onLayout(boolean changed, int left, int top, int right, int bottom) { layoutChildren(left, top, right, bottom, false /* no force left gravity */); //1 } void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) { final int count = getChildCount(); //2 for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() ! = GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int width = child.getMeasuredWidth(); final int height = child.getMeasuredHeight(); int childLeft; int childTop; Layout (childLeft, childTop, childLeft + width, childTop + height); / / 3}}}Copy the code

Four, draw process draw

1, performDraw

The call chain for the method, as shown in side comments 1, 2, and 3 below, will eventually call the View’s draw method;

Private void performDraw() {,,, Boolean canUseAsync = draw(fullRedrawNeeded); //1,,,} private Boolean draw(Boolean drawneeded) {if (! drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) { //2 return false; } } private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) {,,, mview.draw (canvas); //3,,,}Copy the code

2, View draw method

In view of the draw method in the official also given notes, notes that draw process is divided into 7 steps, in the notes also elaborated very clearly, we do not repeat, each step can point into the corresponding method to view, we mainly look at the third and fourth steps;

The third step is to draw itself through the onDraw method. We click on it and find that it is an empty implementation, so we need to implement it in a specific view.

The dispatchDraw(Canvas) method is used to draw all sub-views. In the view, it is an empty implementation. In the ViewGroup, the concrete implementation is given.

public void draw(Canvas canvas) { /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) * 7. If necessary, draw the default focus highlight */ // Step 1, draw the background, if needed drawBackground(canvas); // skip step 2 & 5 if possible (common case) final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) ! = 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) ! = 0; if (! verticalEdges && ! horizontalEdges) { // Step 3, draw the content onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); drawAutofilledHighlight(canvas); // Overlay is part of the content and draws beneath Foreground if (mOverlay ! = null && ! mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); if (isShowingLayoutBounds()) { debugDrawFocus(canvas); } // we're done... return; }}Copy the code

3. DispatchDraw method in ViewGroup

Note 1: drawChild is called in the dispatchDraw method to draw the child view. Note 2: Draw method of each child is called.

protected void dispatchDraw(Canvas canvas) { final int childrenCount = mChildrenCount; ,,, for (int I = 0; i < childrenCount; i++) { while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { final View transientChild = mTransientViews.get(transientIndex); if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() ! = null) { more |= drawChild(canvas, transientChild, drawingTime); //1 } transientIndex++; if (transientIndex >= transientCount) { transientIndex = -1; DrawChild (Canvas, View child, long drawingTime) {return child.draw(Canvas, this, drawingTime); / / 2}Copy the code

Five, pay attention to:

RequestLayout differs from InValidate and postInvalidate

1, requestLayout

After requestLayout is called from the View, parent’s requestLayout is recursively called, eventually to ViewRootImpl’s requestLayout method, as shown in Note 1, and finally to performTraversals to perform the three traversals. Since requestLayout sets The ForceLayout flag to true, onMeasure and onLayout will execute, and onDraw will re-execute based on the upper, lower, and left parameters of the view and whether the data is dirty.

public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); / / 1}}Copy the code

2, invalidate

Invalidate does not set the parameters of the requested layout, so onMesaure and onLayout do not execute, while onDraw does;

3, postInvalidate

When a child thread calls this method to update the view, it actually switches to the main thread via handler, and eventually invalidates it.