The questions we will answer in this article are:

  • How does setContentView work?
  • What is the reason why activities are displayed after onResume?
  • What is ViewRoot? Is it the rootView of ViewTree?

First, the overall flow chart

First map:

To outline the process (and again at the end of this article) :

  1. createPhoneWindowObject toPhoneWindowObject to add aDecorView
  2. forDecorViewBind aViewRootImplBy thisViewRootImplTo be responsible for theViewDraw and refresh
  3. ViewRootImplthroughIWindowSessiontoWMSInitiate a Binder call, andWMSAlso throughIWindowInitiate a call to the application
  4. ViewRootImplWill be inWMSInside register a window, and then byWMSManages the size, location, and hierarchy of all Windows uniformly
  5. So the first time I drew it,ViewRootImplI will also apply for one from WMSSurface(WMStoSurfaceApplication), got itSurfaceAfter that, the application is ready to draw
  6. Once I’ve drawn it on the application side,SurfaceFlingerWill be in accordance with theWMSThe hierarchy and other information provided inside the composite, the final display

In order to let you in the tracking source will not get lost, first with you popular science road on the road of the several “guard brother” :

  • PhoneWindow:WindowThe only implementation class on mobile,WMSManagement is one by oneWindowRather thanView
  • DecorView: View on the top, we usuallysetContentViewSet up theViewThat corresponds to the blue oneContentView.
  • ViewRootImpl: This guy isViewandWMSCommunication bridge, every timeViewWant to talk toWMSCommunication goes through it; Every timeWMSWant to makeViewUpdates also go through it; aDecorViewCorresponds to aViewRootImpl
  • SurfaceFlinger: be responsible forSurfaceThe composition of, one pieceSurfaceIt’s just a canvas, and the application side is actually inSurfaceGraph on
  • WindowManagerService: What we always sayWMS, mainly responsible for the management window,I’m not responsible for drawing the view. The following is aWMSThe main functions of:

By the way, THE source code I use is Android 28.

Let’s start with Action, go

Second, source code analysis

2.1 the setContentView

// Class: --> view.java
public void setContentView(int layoutResId){ getWindow().setContentView(layoutResId) ; . }/ / class: -- -- -- -- > PhoneWindow. Java
public void setContentView(int layoutResID) {
    if (mContentParent == null) { installDecor(); }... mLayoutInflater.inflate(layoutResID, mContentParent); . }Copy the code

You can see that the loading layout is done through the mLayOutInflater.inflate (layoutResID, mContentParent), which has two parameters: layoutId and mContentParent, What is this mContentParent?

MContentParent is initialized via installDecor().

2.2 installDecor

-- - > categories: PhoneWindow.java
 private void installDecor(a) {
     if (mDecor == null) {
     //1、new 一个 DecorView
            mDecor = generateDecor(-1); . }if (mContentParent == null) {
      //2mContentParent = generateLayout(mDecor); . }}//1、new 一个 DecorView
 protected DecorView generateDecor(int featureId) {...return new DecorView(context, featureId, this, getAttributes());
 }
 //2
 protected ViewGroup generateLayout(DecorView decor) {...// Inflate the window decor.
     //3, inflate layout, and add it to the decorView
     mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    //4, find contentView, returnViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); .return contentParent;
 }
 
 ///----->DecorView.java
 void onResourcesLoaded(LayoutInflater inflater, int layoutResource){
      / / 3.
      final View root = inflater.inflate(layoutResource, null); .// Put it below the color views.
      addView(root, 0.new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 }
 
Copy the code

Let’s take a look at the process. The above code mainly does:

  • Create a DecorView that is essentially aFrameLayout
  • Inflate a layout root
  • Add root to the decorView
  • throughfindViewByIdFind the contentParent, which is actually the one with ID R.D.C. tentView

GetWindow ().requestFeature(window.feature_no_title); Why do setContentView() precede setContentView()? Check out the installDecor() method

At this point, is the entire interface loaded? Too naive, this step only initializes the View Tree, the whole interface is not displayed yet, the interface is really displayed depends on what is done in the onResume() lifecycle. Keep following

2.3 handleResumeActivity (IBinder token)

-- -- -- -- > categories: ActivityThread. Java@Override
    public void handleResumeActivity(IBinder token,..){
         // TODO Push resumeArgs into the activity for consideration
        / / 1.
        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;
            if (a.mVisibleFromClient) {
                if(! a.mWindowAdded) { a.mWindowAdded =true;
                    / / 2,
                    wm.addView(decor, l);
                } else{... }}if(! r.activity.mFinished && willBeVisible && r.activity.mDecor ! =null && !r.hideForNow) { 
                    ...
          if (r.activity.mVisibleFromClient) {
                / / 3.r.activity.makeVisible(); }}}Copy the code

The whole process is divided into three steps:

  • The callbackActivitytheonResume()methods
  • callWindowManagertheaddView()methods
  • Set up theActivityforvisible

The next thing we’re going to focus on is the addView() method. Why is that? Since the first step is the callback and the third step is to make it visible, the process that triggers the interface drawing is in the second step. So, we’re going to track step two.

2.4 wm. AddView (decor, l)

---->WindowManagerImpl.java
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
----> WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {... ViewRootImpl root;synchronized (mLock) {
                  root = newViewRootImpl(view.getContext(), display); .// do this last because it fires off messages to start doing things
                  Call this method last because it sends a message to start work
                  try{ root.setView(view, wparams, panelParentView); }... }}Copy the code

This method does two things:

  • To create theViewRootImplObject (so,ViewRootImplandWindowThe relationship is one to one.
  • callViewRootImplThe method setView

2.5 viewRootImpl.setView

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
        if (mView == null) {
                mView = view;
             // 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();
          try{... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); . }... }}Copy the code

The setContentView method focuses on a few things:

  • tomViewThe assignment
  • callrequestlayout
  • callwindowSessiontheaddToDisplay

The first fork in the road comes when we separate the requestLayout () method from the addToDisplay() method.

2.5.1 RequestLayout ()

----> ViewRootImpl.java

 @Override
    public void requestLayout() {
        if(! MHandlingLayoutInLayoutRequest) {/ / check whether it is the main thread. The next time the interviewer asks you where the UI cannot update // exceptions in child threads are thrown, don't be afraid to tell the interviewer, in ViewRootImpl // requestLayout() checkThread(); mLayoutRequested =true;
            //1
            scheduleTraversals();
        }
    }
   //1
    void scheduleTraversals() {
        if(! mTraversalScheduled) { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); . }}Copy the code

In this step, a callback will be posted, which will be called the next time a Vsync signal arrives (see this article on how to callback to the Java layer the next time a Vsync signal arrives).

Anyway, when the Vsync signal arrives, the mTraversalRunnable run method will eventually be executed.

----> ViewRootImpl.java
 final class TraversalRunnable implements Runnable {
        @Override
        public void run(a) { doTraversal(); }}void doTraversal(a) {... performTraversals(); . }private void performTraversals(a) {... relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); .// Draw trilogy: Measure, layout, drawperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec); . performLayout(lp, mWidth, mHeight); . performDraw(); . }Copy the code

Measure, layout, draw these methods we are familiar with, so we focus on relayoutWindow this method to do what? Why is it placed before Measure, layout and DRAW?

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
        // When RemoteException is seen, it is assumed that there is ipc communication.intrelayoutResult = mWindowSession.relayout(... , mSurface); . }Copy the code

In fact, this step is a ———— to WMS to apply for a Surface.

Isn’t there a Surface already? Isn’t that the mSurface?? (Black question mark face ❓)

In fact, this mSurface is an empty Surface, it is just created, but has not allocated memory space, it is still blank.

    public final Surface mSurface = new Surface();
Copy the code

The application side needs to call relayoutWindow of WMS to assign value to Surface. I will not analyze the transfer and assignment process of Surface.

After Surface assignment is successfully initialized, the client is ready to draw. When the drawing is complete, unlockAndPostCanvas() is called to tell SurfaceFlinger to compose.

Now that the requestLayout() method is done, go to another fork in the road.

Points forks in the road 2.5.2 windowSession. AddToDisplay ()

WindowSession is an AIDL, which is used to communicate between applications and WMS. Its real implementation class is Session, specifically looking for the process as follows:

 ---> Session.java
@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,...) {
    // Note that IWindow is a WMS call to Binder
    //Session passes it to WMS so that WMS can initiate calls to the application
        return mService.addWindow(this, window, seq, attrs, ...) ; }Copy the code

As you can see, the addView method of WMS is finally called. The function of this method is to register Window objects with WMS, and then WMS coordinates the hierarchy, size, and position of these Windows.

For WMS, it does not care about the Window object or View object of the application itself. For WMS, one of its important functions is to assign Surface to the Window of the application and control the display order, position and size of these surfaces.

When the application is finished drawing the Surface, SurfaceFlinger will synthesize the image data according to the size, level and position provided by WMS, and finally write it into the screen buffer and draw it.

2.6 summary

handleResumeActivity()

Third, summary

To summarize, the whole process is complete:

  1. To create thePhoneWindowObject toPhoneWindowObject to add aDecorView
  2. forDecorViewBind aViewRootImplBy thisViewRootImplTo be responsible for theViewDraw and refresh
  3. ViewRootImplthroughIWindowSessiontoWMSInitiate a Binder call, andWMSAlso throughIWindowInitiate a call to the application
  4. ViewRootImplWill be inWMSInside register a window, and then byWMSManages the size, location, and hierarchy of all Windows uniformly
  5. So the first time I drew it,ViewRootImplI will also apply for one from WMSSurface, with theSurfaceAfter that, the application is ready to draw
  6. And when I’ve drawn it,SurfaceFlingerWill be in accordance with theWMSThe hierarchy and other information provided inside the composite, the final display

Iv. Problem solving

  • setContentViewWhat is the principle of?

The principle of setContentView() is that (1) you create a DecorView and ViewRootImpl and bind them together, and (2) you create a ViewTree through the inflate method, which is not displayed yet

  • ActivityinonResumeWhat’s the reason why it’s not shown until later?

The onResume method calls ViewRootImpl’s performTraversal() method for rendering the interface and makeVisible for displaying the interface;

  • ViewRootWhat is it? Is it the RootView of ViewTree?

View wroot, or its implementation class View Wroot PL, has nothing to do with views. The view wroot is only the manager of the ViewTree, not the root node of the ViewTree. The real root node is the DecorView.

5. Reference documents

  • In-depth understanding of control systems
  • Lesson for video
  • Android Window mechanism

Dregs programmer a, uneducated, mistakes and omissions please forgive me ~