"Adventure of Activity display interface" - talk about those unclear relationships of View

preface

In the process of Activity displaying View, some important roles are always confused, such as PhoneWindow, DecorView and ViewRootImpl.

There are often interview questions that ask, what is the relationship between the four of them? When was it created? When did View draw for the first time? And so on.

Today, let's start from the start of Activity and show what steps will be taken in the whole process of View, and what is the relationship between the roles.

Animation display

In order to facilitate your understanding, first show you the relationship between these people in the form of animation:

Source code analysis

From the birth of love

Before the Activity interface was displayed, it was still an Activity we couldn't see. I gave it a nickname - Xiaoai

How was little love born? Everyone familiar with the Activity startup process knows that the creation of Xiaoai takes place in performLaunchActivity:

//ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //Create ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //Create Activity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        }

        try {
            if (activity != null) {
                //Complete the initialization of some important data of activity
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);

                //Call the onCreate method of the activity
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
            }
        }

        return activity;
    }

In this process, three main things have been done:

  • The Activity is instantiated
  • The attach method was called for initialization
  • Call the onCreate method to load the layout from the layout file and make preparations for View display.

Find Xiaoai a helper to interact with View (PhoneWindow)

As we all know, after Xiaoai was created, she was busy and certainly couldn't manage every View by herself, so he found a helper to help her interact with and manage View.

(decoupling of Activity and View)

What is this helper? It is the Window, that is, the implementation class PhoneWindow.

This process occurs in the attach method:

//Activity.java
final void attach() {
		//Create PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setCallback(this);
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        
    }

In order to facilitate memory, we call this PhoneWindow housekeeper window housekeeper.

Load layout file (DecorView)

With the window manager, you can continue the onCreate method. The most important thing in the onCreate method is the setContentView.

The View in the layout file can be loaded through setContentView.

As mentioned earlier, the management of View is left to the window manager, so we directly call the setContentView method of PhoneWindow:

	//Activity.java
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

Then you start loading the layout file.

However, considering this, activities have different themes, and different themes have different layout structures. Therefore, we have to set a top-level View as the boss of all views before loading the layout file set by ourselves.

The top-level View is DecorView. For convenience, I call it the top little brother, or little brother for short.

See how the younger brother DecorView was created:

//PhoneWindow.java
    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } 


        if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
    }


    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1);
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);       
        }
    }


    protected DecorView generateDecor(int featureId) {
        return new DecorView(context, featureId, this, getAttributes());
    }

In this way, the younger brother DecorView is created, and then it's time for the younger brother to work.

As mentioned above, what's the purpose of DecorView?

To set different layout structures according to different topics, this work takes place in the generateLayout method. I won't analyze the details today.

Looks like my little brother's work is finished?

Wait, the application's own layout hasn't been loaded yet, and the important things haven't started yet.

Go back to the setContentView method above. After calling the installDecor method to create a younger brother, you also do one thing:

	//Load xml layout file
	mLayoutInflater.inflate(layoutResID, mContentParent);



    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

And this inflate is the well-known method of loading layout files. Pass in the xml layout file, parse and combine the passed in parent view - mContentParent, convert it into a complete tree structure, and finally return to the top-level view.

At this point, setContentView is finished,

To put it simply, we created DecorView, and combined the top-level view with the xml layout file we passed in to generate a multi-layer view.

Show this View (ViewRootImpl)

With the View, the structure has been determined. The next step is how to display the View structure and let our mobile phone display the picture?

Yes, it's drawing.

Who is better to do the drawing of View? Recall the current members:

  • Xiaoai Activity: big boss, responsible for overall planning.
  • Window manager PhoneWindow: responsible for managing all views.
  • DecorView: the top-level View, which is responsible for displaying the theme layout.

There seems to be no one to be in charge of View drawing? Drawing is so important that we need to recruit another friend.

ViewRootImpl flashes ✨ On stage, for convenience, I call him Xiao Wei.

When was Xiaowei founded?

Next, let's look at the calling process of Activity. After onCreate is called, onResume method will be called, which starts with handleResumeActivity method.

    @Override
    public void handleResumeActivity() {
        //onResume
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        //addView
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes()
            wm.addView(decor, l);
        }

This method mainly does two things:

  • Call onResume method
  • Call the addView method of WM.

Wei doesn't seem to be out yet?

Continue to look at the addView method:

//WindowManagerGlobal.java
	public void addView() {
        
        synchronized (mLock) {
            
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);


            try {
                root.setView(view, wparams, panelParentView);
            } 
        }
    }



    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mThread = Thread.currentThread();
    }


Finally, Xiaowei's ViewRootImpl has been created. In this ViewRootImpl, two variables deserve attention:

  • mWindowSession. IWindowSession is a Binder object used for inter process communication. Its implementation on the server side is Session, which can be used to complete WMS related work.
  • mThread. Set the thread variable to the current thread, that is, the thread when instantiating ViewRootImpl. Generally, when updating the UI with different threads, you will judge whether the current thread and mthread are equal. If they are different, an exception will be thrown.

Next, call the setView method of ViewRootImpl, which is naturally the way Xiao Wei does things with ViewRootImpl:

//ViewRootImpl.java
    public void setView() {
        synchronized (this) {
        	//draw
        	requestLayout();

        	//Call the addWindow method of WMS
        	res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);

        	//Set this(ViewRootImpl) as the parent of view(decorView)
			view.assignParent(this);
        }
    }

There are three main functions:

  • Trigger drawing (specifically including measurement, layout and drawing)
//ViewRootImpl.java
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

    ->scheduleTraversals()
    ->performMeasure() performLayout() performDraw()
    ->measure,layout,draw method
  • Call addWindow method of WMS through Binder

The addToDisplay method will eventually use the addWindow method of the WMS process to assign a Surface to the window, which is responsible for displaying the final interface and will eventually be drawn on the screen.

  • Set ViewRootImpl as the parent of decorView

After this setting, when the child view requests drawing (requestLayout), it can always find the ViewRootImpl through the parent, and then the ViewRootImpl is responsible for the drawing of all views.
The whole calling process is:

View.requestLayout -> DecorView.requestLayout -> ViewRootImpl.requestLayout

//View.java
    public void requestLayout() {
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
    }

Summary

At this point, the Activity has finally completed its startup life cycle, the interface is also displayed, and Xiaoai has become a formed Activity.

Although there are many intermediate roles, each role is indispensable:

Because the View needs to be managed, PhoneWindow is created;
Because different layout structures need to be displayed according to the theme, the root View DecorView is created;
Because various events of View need to be handled, including drawing and event distribution, ViewRootImpl is created.

Everyone is busy and obedient to Activity.

exercises

In the past, when I was in class, I always liked to do some exercises after learning knowledge. Today, I also brought you a few questions to consolidate my knowledge.

The relationship among Activity, PhoneWindow, DecorView and ViewRootImpl?

  • PhoneWindow: it is the middle layer of interaction between Activity and View and helps Activity manage View.
  • DecorView: the topmost View of all views and the parent of all views.
  • ViewRootImpl: used to handle View related events, such as drawing and event distribution. It is also the parent of DecorView.

The creation time of the four?

  • The Activity is created in the performLaunchActivity method and is triggered when startActivity.
  • PhoneWindow is also created in performLaunchActivity method, and more specifically, the attach method of Activity.
  • DecorView, created in setcontentview - > phonewindow installDecor.
  • ViewRootImpl is created in the handleResumeActivity method and finally created through addView.

When did the first drawing of View happen?

The first drawing takes place in the handleResumeActivity method. Through the addView method, ViewRootImpl is created and its setView method is called.

Finally, the requestLayout method is invoked to start the layout, measurement and rendering process. Base note:

Why does the thread update UI crash?

In the trigger drawing method requestLayout, there is a checkThread method:

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }  

The mThread is compared with the current thread. mThread is assigned when ViewRootImpl is instantiated.

Therefore, the reason for the crash is that the thread when the view is drawn to the interface (that is, the thread when the ViewRootImpl is created) and the thread when the UI is updated are not the same thread.

reference resources

Android advanced decryption
View these 12 questions

bye-bye

Thank you for reading. A little buddy can learn about my official account code blocks. ❤️❤️
One knowledge point a day makes a lot, and a knowledge architecture is established.
There are a group of good Android partners here. Welcome to join ~

Posted by Lphp on Sun, 17 Apr 2022 05:39:34 +0930