The past and present of Android's top-level view DecorView

The ActivityThread#performLaunchActivity method is executed during the startup process of the Activity, which calls Activity#attach. In the attach() method, the only implementation class PhoneWindow that instantiates the mWindow property held by the Activity is Window.

    ActivityThread#performLaunchActivity
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        activity.attach(...);
        ...
    }

    Activity#attach
    final void attach(...) {
        ...
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowManager(...);
        mWindowManager = mWindow.getWindowManager();
        ...
    }

In ActivityThread#handleResumeActivity, after calling Activity#onResume through ActivityThread#performResumeActivity method, DecorView will be obtained and added to ViewRootImpl through WindowManager.

    final void handleResumeActivity(...) {
        ...
        r = performResumeActivity(token, clearHide, reason);
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        a.mDecor = decor;
        wm.addView(decor, l);
        ...
        r.activity.makeVisible();
        ...
    }

     PhoneWindow#getDecorView
     public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

r.activity.getWindow() returns the PhoneWindow object instantiated in Activity#attach. Then call PhoneWindow#getDecorView to get the DecorView object. For details, see Android setContentView() source code analysis . Then fill the decorView but set it to be invisible (set visible via r.activity.makeVisible()), and finally add the decorView to ViewRootImpl via WindowManager#addView. ViewRootImpl will be analyzed in detail below. For the process of addView, please refer to Android using WindowManager to implement floating window and source code analysis . Careful students may notice that the DecorView has just been added to the ViewRoot through WindowManger#addView at this time. In fact, this also explains why "in the process from onCreate to onResume, the Activity has been visible to the system, but it has not been displayed on the interface". reason. At this time, because the Activity is loaded and the attach, onCreate, onStart, and even onResume have been executed, the DecorView has not been added to the Window. It is also because of this reason that the width and height of the controls cannot be obtained in onCreate, onStart, and onResume. After WindowManager#addView (internally instantiated ViewRoot), layout, measure, and draw are actually executed.

DecorView is a subclass of FrameLayout, and to put it bluntly, it also inherits from View. The View/ViewGroup in the rest of the XML is added to the DecorView, which belongs to the "Add View/ViewGroup to the ViewGroup" type, which is not representative. If you are interested, you can refer to the source code analysis of the Android XML layout file parsing process .

If the setContentView() method is called in Activity#onCreate, the DecorView will be created directly. If the setContentView() method is not called, the DecorView will be automatically created by r.window.getDecorView() in ActivityThread#handleResumeActivity. In short, whether you create it or not, there will always be a DecorView created by PhoneWindow in the Activity. The role of PhoneWindow is to operate View. Activity calls findViewById and similar methods to indirectly operate View through PhoneWindow. In this way, DecorView, as the top-level view of the View tree, is associated with Activity through PhoneWindow.

In the process of WindowManger#addView, ViewRootImpl#addView is called.

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                ...
                mView = view;
                requestLayout();
                res = mWindowSession.addToDisplay(...);
                ...
                }
            }
    }

First assign the passed View to the global property mView, and then use the mView property to operate the DecorView. The process of mWindowSession.addToDisplay has been parsed in Android's use of WindowManager to implement floating windows and source code analysis . The brief description is to add window to the mWindowMap property of WindowManagerService for management. The requestLayout() method is amazing. Maybe you can customize the View. Maybe you have seen the three major processes of View drawing (measure, layout, draw), but do you know the source of these things? That's right, it's all here in requestLayout(). Inserting a digression, through the above code, it can be found that PhoneWindow does not directly operate DecorView, and there is a ViewRootImpl in the middle.

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

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

Usually updating the UI in the child thread will explode the error in checkThread. But look at this method carefully, mThread is instantiated when ViewRootImpl is initialized, which is equal to the thread that created ViewRootImpl. Under normal circumstances, we create ViewRootImpl in the main thread, but in fact, it can also be created in the child thread, such as creating Toast. Toast is also a kind of window, refer to Android Advanced Custom Toast and source code analysis . But Thread.currentThread() is the thread when the UI is updated. What if these two are in child threads at the same time? Nothing will happen. Yes, child threads can update UI. However, there are requirements for the UI creation thread. Only the ViewRootImpl created by the child thread can update the UI in the child thread. The sample code for the child thread to update the UI is as follows:

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                // Toast.makeText(MainActivity.this,"一口仨馍",Toast.LENGTH_SHORT).show();
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("Game of Thrones")
                        .setMessage("winter is coming")
                        .setPositiveButton("yes,my lord", null)
                        .show();
                Looper.loop();
            }
        }).start();

The scheduleTraversals() method is called layer by layer (mTraversalRunnable->doTraversal->performTraversals)

    private void performTraversals() {
        final View host = mView;
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        performLayout(lp, mWidth, mHeight);
        performDraw();
    }

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

     private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
         final View host = mView;
         host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());       
    }

    private void performDraw() {
        // draw(fullRedrawNeeded) --> drawSoftware
        mView.draw(canvas);
    }

The performTraversals method is very long. Here, only the starting point of the three major processes of View drawing is intercepted. mView is the previously cached DecorView. After that, the View's measure, layout, draw, onMeasure, onLayout, and ondraw are started. . .

For more Framework source code analysis, please move to the Framework source code analysis series [Contents]

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325568817&siteId=291194637