Introduction to the

ViewRootImpl is the highest level of View and is the root of all views. ViewRootImpl implements the required protocol between View and Windows Manager. The ViewRootImpl creation process starts in Windows ManagerImpl. View measurement, layout, drawing, and screen loading all start in the View wrotim PL.

Let’s take a look at this:

  • Window

    We know that all the elements in the interface are made up of views, which are attached to the Window. Window is just an abstract concept. The interface can be abstracted as a Window, or it can be abstracted as a View.

  • ViewManange

    An interface that internally defines three methods for adding, updating, and deleting views.

    public interface ViewManager
    {
        public void addView(View view, ViewGroup.LayoutParams params);
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);
        public void removeView(View view);
    }
    Copy the code
  • WindowManager

    ViewManager is also an interface that inherits from ViewManager. In an application, Windows are managed through WindowManager, attaching the View to the Window. It has an implementation class, WindowManagerImpl.

  • WindowManagerImpl

    WindowManager implementation class, WindowManagerImpl internal method implementation are through the proxy class WindowManagerGlobal to complete.

  • WindowManagerGlobal

    Windows ManagerGlobal is a singleton, that is, there is only one Windows ManagerGlobal object in a process that serves views for all pages.

  • ViewParent

    An interface that defines the responsibilities that will become the parent class of the View.

  • ViewRootImpl

    Top of the view hierarchy. A Window corresponds to a VIew VIew and a VIew. This View is operated on by View View PL.

A small chestnut, we will only create a Window object in Actiivty. The View in the setContentView method is eventually added to the DecorView in the Window object, meaning that there is one View in each Window. This View is operated on by RootViewImpl.

WindowManager is the entry point. Add a Window via WindowManager’s addView, and then create a ViewrotimPL to manipulate the View and render the View to a Window on the screen.

For example, in an Activity, after the onResume execution is complete, the DecorView in the Window is retrieved and added to the Window through the WindowManager. This process is done by RootViewImpl to measure, draw, and so on.

If you are not familiar with Windows or Windows Manager, take a look at this article

Initialization of RootViewImpl

The three main processes of a View are done by RootViewImpl. In ActivityThread, after the Activity object is created, after onResume, The DecorView is added to the window via WindowManager, creating the ViewRootImpl in the process:

ActivityThread.handleResumeActivity

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {

    / /...
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    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;
        // Set the window type to application type
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  
        if (a.mVisibleFromClient) {
            if(! a.mWindowAdded) { a.mWindowAdded =true;
                // Add decor to window
                wm.addView(decor, l);
            } else{ a.onWindowAttributesChanged(l); }}}//....
}
Copy the code

WindowManagerImpl.addView

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
Copy the code

WindowManagerGlobal.addView

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    // 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(! (paramsinstanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
  	/ /...
    
    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
   
		/ / create ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);
		
        // Add the View, ViewRootImp, params corresponding to the Window to the list. This step is convenient for updating and deleting the View
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            // Set the View corresponding to the Window to the created ViewRootImpl
            // Update the interface with ViewRootImpl and add it to the WIndow.
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throwe; }}}Copy the code

The ViewRootImpl operates on the View

ViewRootImpl.setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            attrs = mWindowAttributes;
   
      		// Request layout, execute View drawing method
            requestLayout();
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = newInputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) ! =0;
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                // Add Window to the screen. MWindowSession implements the IWindowSession interface, which is the Binder object of Session.
                // addToDisplay is an AIDL procedure that tells WindowManagerServer to add a Window
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            }
			// Set the Parent of the current View
            view.assignParent(this); }}}Copy the code

ViewRootImpl.requestLayout

// Request layout
public void requestLayout(a) {
    if(! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested =true; scheduleTraversals(); }}// Check the current thread, if not the main thread, directly throw an exception
void checkThread(a) {
    if(mThread ! = Thread.currentThread()) {throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views."); }}final class TraversalRunnable implements Runnable {
    @Override
    public void run(a) { doTraversal(); }}final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

void scheduleTraversals(a) {
    if(! mTraversalScheduled) { mTraversalScheduled =true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

void doTraversal(a) {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false; }}}Copy the code

In the above code, requestLayout is called to request the layout, which will eventually be executed in the performTraversals method. This method calls the View’s measure(), layout(), and draw() methods.

ViewRootImpl. performTraversals

private void performTraversals(a) {
    // cache mView since it is used so much below...
    final View host = mView;

	// Want to show the width and height of the Window
    int desiredWindowWidth;
    int desiredWindowHeight;
	// This is the first execution
    if (mFirst) {
		//....
    
   		// Append window information to the View.
        host.dispatchAttachedToWindow(mAttachInfo, 0);
		//....
    } else {
        desiredWindowWidth = frame.width();
        desiredWindowHeight = frame.height();
        if(desiredWindowWidth ! = mWidth || desiredWindowHeight ! = mHeight) {if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
            mFullRedrawNeeded = true;
            mLayoutRequested = true;
            windowSizeMayChange = true; }}// Start preparing the layout
    booleanwindowShouldResize = layoutRequested && windowSizeMayChange && ((mWidth ! = host.getMeasuredWidth() || mHeight ! = host.getMeasuredHeight()) || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && frame.width() < desiredWindowWidth &&  frame.width() ! = mWidth) || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && frame.height() < desiredWindowHeight && frame.height() ! = mHeight)); windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM; windowShouldResize |= mActivityRelaunched;final boolean computesInternalInsets =
            mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
            || mAttachInfo.mHasNonEmptyGivenInternalInsets;

    if(mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params ! =null || mForceNextWindowRelayout) {
        mForceNextWindowRelayout = false;
		//....
        if(! mStopped || mReportNextDraw) {booleanfocusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) ! =0);
            if(focusChangedDueToTouchMode || mWidth ! = host.getMeasuredWidth() || mHeight ! = host.getMeasuredHeight() || contentInsetsChanged || updatedConfiguration) {int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        
                if (measureAgain) {
					/ / the View of measurement
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                }

                layoutRequested = true; }}}/ /...
    final booleandidLayout = layoutRequested && (! mStopped || mReportNextDraw);boolean triggerGlobalLayoutListener = didLayout
            || mAttachInfo.mRecomputeGlobalAttributes;
    if (didLayout) {
        // View layout
        performLayout(lp, mWidth, mHeight);
		/ /...
    }

	/ /...
    booleancancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || ! isViewVisible;if(! cancelDraw) {// View drawing
        performDraw();
    } else {
        if (isViewVisible) {
            // Try again
            scheduleTraversals();
        } else if(mPendingTransitions ! =null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
                mPendingTransitions.get(i).endChangingAnimations();
            }
            mPendingTransitions.clear();
        }
    }

    mIsInTraversal = false;
}
Copy the code

The View of measurement

The ViewRootImpl calls performMeasure to perform the View measurement corresponding to the Window.

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

So inside the performMeasure method it’s going to execute the measure method of the View, which is going to evaluate the constraint information, and then it’s going to call the onMeasure method,

The layout of the View

ViewRootImpl calls performLayout to perform the layout of the View corresponding to the Window

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mScrollMayChange = true;
    mInLayout = true;


    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // Call the Layout method for the layout
        host.layout(0.0, host.getMeasuredWidth(), host.getMeasuredHeight());

        mInLayout = false;
        int numViewsRequestingLayout = mLayoutRequesters.size();
        if (numViewsRequestingLayout > 0) {
      
            ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                    false);
            if(validLayoutRequesters ! =null) {
   
                mHandlingLayoutInLayoutRequest = true;

                // Process fresh layout requests, then measure and layout
                int numValidRequests = validLayoutRequesters.size();
                for (int i = 0; i < numValidRequests; ++i) {
                    final View view = validLayoutRequesters.get(i);
                    Log.w("View"."requestLayout() improperly called by " + view +
                            " during layout: running second layout pass");
                    // Request that the View be laid out. This action will cause the View to be remeasured, laid out, and drawn
                    view.requestLayout();
                }
                measureHierarchy(host, lp, mView.getContext().getResources(),
                        desiredWindowWidth, desiredWindowHeight);
                mInLayout = true;
                host.layout(0.0, host.getMeasuredWidth(), host.getMeasuredHeight());

                mHandlingLayoutInLayoutRequest = false;

                // Check the valid requests again, this time without checking/clearing the
                // layout flags, since requests happening during the second pass get noop'd
                validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                if(validLayoutRequesters ! =null) {
                    final ArrayList<View> finalRequesters = validLayoutRequesters;
                    // Post second-pass requests to the next frame
                    getRunQueue().post(new Runnable() {
                        @Override
                        public void run(a) {
                            int numValidRequests = finalRequesters.size();
                            for (int i = 0; i < numValidRequests; ++i) {
                                final View view = finalRequesters.get(i);
                                // Request that the View be laid out. This action will cause the View to be remeasured, laid out, and drawnview.requestLayout(); }}}); }}}}finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}
Copy the code

As can be seen from the above code, during layout layout, it is possible to re-measure the View for requestLayout, layout, and drawing.

The View of the drawing

ViewRootImpl draws the View corresponding to the Window by calling performDraw.

private void performDraw(a) {
    if(mAttachInfo.mDisplayState == Display.STATE_OFF && ! mReportNextDraw) {return;
    } else if (mView == null) {
        return;
    }

    final boolean fullRedrawNeeded =
            mFullRedrawNeeded || mReportNextDraw || mNextDrawUseBlastSync;
    mFullRedrawNeeded = false;

    mIsDrawing = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");

    boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
    addFrameCallbackIfNeeded();

    try {
        / / to draw
        boolean canUseAsync = draw(fullRedrawNeeded);
        if(usingAsyncReport && ! canUseAsync) { mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
            usingAsyncReport = false; }}finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
	/ /...
}
Copy the code
private boolean draw(boolean fullRedrawNeeded) {
    / /...
 
	// Notify the View of the registered draw event
    mAttachInfo.mTreeObserver.dispatchOnDraw();

    boolean useAsyncReport = false;
    if(! dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {if (isHardwareEnabled()) {
          	// Call the View invalidate method corresponding to the Window
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
        } else {
          	/ / draw the View
            if(! drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) {return false; }}}if (animating) {
        mFullRedrawNeeded = true;
        scheduleTraversals();
    }
    return useAsyncReport;
}
void invalidate(a) {
    mDirty.set(0.0, mWidth, mHeight);
    if (!mWillDrawSoon) {
        scheduleTraversals();
    }
}
Copy the code
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

    // Draw with software renderer.
    final Canvas canvas;
    try {
        canvas = mSurface.lockCanvas(dirty);
        // TODO: Do this in native
        canvas.setDensity(mDensity);
    } 

    try {
        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
            Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                    + canvas.getWidth() + ", h=" + canvas.getHeight());
            //canvas.drawARGB(255, 255, 0, 0);
        }
		/ / the View map
        mView.draw(canvas);

        drawAccessibilityFocusedDrawableIfNeeded(canvas);
    } finally{}return true;
}
Copy the code

The resources

Android development art exploration

ViewRootImpl monologue

Recommended reading

  • Android | understand Window and WindowManager

If this article is helpful to you, please give it a thumbs up. Thank you! If you have any questions, please feel free to comment directly below. Thank you!