The main purpose of this article: to explore the Android interface display mechanism, to provide insights into the Android view hierarchy, and to discover that the real use of WindowsManager is to manage decorViews on Windows, not Windows. RequestWindowFeature (window.feature_no_title) must be placed in front of the setContentView() method to set the full-screen display. There are custom view onMeasure, onLayout and onDraw method call timing.

Body: If you want to present an interface, the typical workflow for Android engineers is to design a layout file based on a prototype of the product, load the layout file into the setContentView of the Activity, initialize various Views, compile and run, and when you start the Activity, You can see a view of the layout file. So the question is, how does Android display our custom layout file?

What most programmers think of as the view level of Android





What most programmers think of as the view level of Android

DecorView is a top-level view, blah blah blah… I don’t agree at all with the view hierarchy represented by this graph, so to speak, it is wrong (just a personal opinion, welcome to discuss) why? Since we’re talking about View levels and DecorView is a top-level View, I think we can remove both Window and Activity and leave it like this.





Android’s true view hierarchy

. At this point, some of you might get excited. What the hell is that? The Window? Window is a Window. WindowsManager manages Windows.

Start with a simple layout file

To break the boredom, let’s start with a small example: Boss: When I click on this App, MY photo should be displayed on the interface! Five minutes! Coder: Ok, I’ll do it. The programmer then deftly opens the IDE and defines the bossActivity_boss_layout.xml custom layout file for bossActivity.java

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.test.MainActivity" >
    <ImageView 
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:src="@drawable/activity_boss"/>
 </RelativeLayout>Copy the code

I’m going to set the boss’s photo as the background and then I’m going to set the layout file in the onCreate method of the BossActivity

 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_boss_layout);
}Copy the code

Configure as the main startup class, compile, execute, ok, boss’s avatar is out, done! Three minutes! We see that the layout file is set in the setContentView method, so let’s go into this function

public void setContentView(@LayoutRes int layoutResID) {
    getWindow(a).setContentView(layoutResID);
    initWindowDecorActionBar(a); }Copy the code

So this is getWindows setContentView method that’s called, so let me see getWindo what is this Windows, and where is it initialized

public Window getWindow(a) {
    return mWindow;
}Copy the code

Where is mWindow initialized? Moving on, we see that it’s initialized in the Attach method of Activiy, which is a PhoneWindow class.

 final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config.String referrer, IVoiceInteractor voiceInteractor) {
    attachBaseContext(context);
/ /... Leave out some code
    mWindow = new PhoneWindow(this);
/ /... Leave out some code
}Copy the code

So, the PhoneWindow setContentView method is called here.

 public void setContentView(int layoutResID) {
 // Build the DecorView when mContentParent is empty and initialize the mContentParent
    if (mContentParent == null) {
        installDecor();
    } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }/ /... Leave out some code
       mLayoutInflater.inflate(layoutResID, mContentParent);
 / /... Leave out some code
}Copy the code

1. Initialize the DecorView if the mContentParent is empty. 2. The resource file corresponding to this ID is parsed into a View via the LayoutInflate service and added to its parent View (mContentParent). The process by which the LayoutInflate service parses the resource file corresponding to this ID into a view (that is, mLayOutinflater.inflate (layoutResID, mContentParent); This process), you can pay attention to my follow-up blog (there are too many work in the company recently, I don’t have much time to write) and take a look at initDecor().

 private void installDecor(a) {
    if (mDecor == null) {
        mDecor = generateDecor();// Initialize the DecorView here
     / /... Leave out some code
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);// contentParentView is generated
   / /... Leave out some code
}Copy the code

Here we know that the view parsed from the layout file must be added to the mContenParent view, and that the mContentParent is generated through the DecorView, but what is the relationship between the three? Here we do not analyze from the specific code, too much analysis is easy to dizzy, directly from the application display View View analysis. Here we use the Hierarchy Viewer tool. In the Hierarchy diagram, we first find our custom layout, which is a relative layout and contains an ImageView





Enter a description of the picture here

Moving up, what is FrameLayout? It contains our custom layout file, the mCotentParent view.





Enter a description of the picture here

So what’s the parallel layout?





Enter a description of the picture here

This is where our ActionBar is laid out, so we don’t have to analyze what’s inside.

Looking further up, this view is a viewGroup, but not yet a top-level view, we look at its Id, decor_content_parent, which should be the parent layout hosting actionBar and mCotentParent. It looks like mContentParent is not a child of a DecorView. There is a view in between, so mContentParent should be a child of a Decor child view.





Enter a description of the picture here

Moving up, the last FrameLayout is the top-level view, where the DecorView sits.





Enter a description of the picture here

So the relationship between DecorView, mContentParent, and layout file View is that DecorView is the top-level View, and mContentParent is the child View of a DecorView’s child View. The layout file view is a child of mContentparent. When we want to display our layout view, we just display the DecorView. (It also proves my previous point that the view hierarchy does not include Activit and Window. Because an Activity only contains a Window object, and the Window contains a DecorView, the real view is just a DecorView. RequestWindowFeature (window.feature_no_title) to set the full screen display must be placed in front of the setContentView() method. That’s because initializing actionBar and so on is done in setContentView.)

Let the DecorView display

Display the Activity (Dialog) (show method)

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume) {

    ActivityClientRecord r = performResumeActivity(token, clearHide);
    if(r ! =null) {
        final Activity a = r.activity;
        if (r.window= =null && !a.mFinished && willBeVisible) {
            // 1 这里获取了windows
            r.window = r.activity.getWindow();
            // 2 Here we get the decorView
            View decor = r.window.getDecorView();
            // 3 Set decorView to display
            decor.setVisibility(View.INVISIBLE);
            // 4 get the WindowManager WindowManager service
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                // 4 Display the decorViewwm.addView(decor, l); }}}// Omitted some code
}Copy the code

The decor added to wm is what makes the decorView show up. Why didn’t I say add? This is not an add-on, just a Window Manager that manages and displays the decorView. Here, we can clearly see that Windows Manager does not manage the Window, but the Decorview inside the Window. Where does the addView method come from? Code analysis reveals that the concrete implementation class of the WindowManger service is WindowMangagerImpl

    public void addView(View view, ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}Copy the code

AddView (View, Params, mDisplay, mParentWindow); MGlobal is the WinowMangerGolbal class, so let’s look at its addView method.

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    // Omit some code
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
        // Omit some code
        // 1. Initialize ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        // 2. Set parameters to the decorView
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }
    try {
        // 3 Call the ViewRootImpl setView method to display the decorView
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {

    }
}Copy the code

As you can see, instead of an addView procedure, you end up calling the setView method of the ViewRootImpl class.

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
            // A lot of code is omitted
            requestLayout();
            try {
                / / display decorviewres = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); }}}Copy the code

RequestLayout is the requestLayout that draws the view to be displayed

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

It checks if the thread is not the current thread, and if it’s not, it gets an error, and that’s why you can’t update the UI on a child thread, but you can set view on a child thread like onCreate or onResume, Because performResumeActivity is executed before it, requestLayout has not yet been executed.

void scheduleTraversals(a) {
    if(! mTraversalScheduled) { mTraversalScheduled =true;
        mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
    }
}
final class TraversalRunnable implements Runnable {
    @Override
    public void run(a) { doTraversal(); }}void doTraversal(a) {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        try{ performTraversals(); }}}Copy the code

This will eventually lead to the execution of the common FormTraversals method

private void performTraversals(a) {
    // Omitted some code
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    // Omitted some code
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    // Omitted some code
    performDraw(a);
}Copy the code

The draw method in performDraw is more complex, which involves notifying surfaceFlinger of updates.

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

}Copy the code

The view is measured, positioned, and painted in the performTraversals method. This is why we usually customize views by modifying onMeasure, onLayout and onDraw methods.

Once the content is drawn, Through the res = mWindowSession. AddToDisplay (mWindow mSeq, mWindowAttributes, getHostVisibility (), mDisplay. GetDisplayId (), mAttachInfo.mContentInsets, mInputChannel); Notifications and WindowManagerService (Native layer), which displays the drawn view. The Decorview can then be displayed in the mobile interface. The connection between mWindowSession and WindowManagerService (Native layer) is done in the viewRootImpl object constructor, which can be analyzed by yourself.

To summarize, the Activity loads the layout file as a view through setContentView and attaches it to a Decorview as a child of a Decorview subview. Display the Decorview through a WindowManager in the handleResumeActivity, where the view is drawn and the Process of telling the WindowManagerService in the Native layer to display the Decorview, This is done through view script IN Windows ManagerGlobal.