preface

View drawing can be said to be the necessary skills of Android development, but the knowledge point about View drawing is also a little complicated. If we start from scratch to read the source code, often a myriad of threads, miss the point. Now when we write a page, the layout is written in XML, we can think about: what happens to the layout from XML to display to screen, and what parts can it be divided into? We break down the entire display process into the following parts

  1. How does the code come fromXMLConverted toView?
  2. ViewHow was it added to the page?
  3. In memoryViewHow exactly is it drawn?
  4. ViewHow does it show up on the screen after it’s drawn?

The contents of this paper are as follows:

1. XMLHow is it converted toView?

As we all know, in Android you write a layout in XML, and then you configure it in the page using the setContentView method and it looks like that’s where the XML gets converted into a View

1.1 setContentViewWhat did you do

    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }
Copy the code

As you can see, resId is passed to the familiar LayoutInflater method, so it seems that converting XML to View is implemented in LayoutInflater methods

1.2 LayoutInflaterWhat did you do?

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        // Precompile returns directly to view, currently not enabled
        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
        if(view ! =null) {
            return view;
        }
        XmlResourceParser parser = res.getLayout(resource);
        try {
        	// Convert 'XML' to 'View'
            return inflate(parser, root, attachToRoot);
        } finally{ parser.close(); }}Copy the code

The code is relatively simple, so let’s analyze it

  1. The first thing we need to be clear about is thatXMLintoViewIt involves time-consuming operations such asXMLParsing is aioOperation,XMLintoViewIt’s also time consuming when it comes to reflection
  2. We can see that before parsing there is atryInflatePrecompiledMethod, which is intended to precompile directly at compile timeXML, returns the built directly at runtimeView, it looks likeGoogleHope to solve this wayXMLPerformance problems. However, this feature is not currently enabled, so this method returns directlynull, currently in effect or the following method
  3. Real willXMLResolve toViewOr ininflateMethod, convert the tag name toViewThe name of the,XMLThe various properties in theAttributeSetObject, which is then generated by reflectionViewobject

Because of the lack of space, I won’t paste the source code for the inflate method here, but notice that setFactory and setFactory2 methods will call these methods to try to create a View before they actually reflect, and the system has an API open. We can customize the way we parse XML and that gives us some room for section-based programming, so we can use these two apis to do things like skin, replace fonts, replace views, speed up View construction, and so on

1.3 summary

Converting XML into a View is done mostly with LayoutInflators, converting tag names into View names, converting various attributes in XML into AttributeSet objects, and then generating View objects through reflection. We can optimize this process in a variety of ways, such as shifting the reverse time to compile time. For those who are interested, please refer to “Step back” layout loading optimization for Android

2. ViewHow was it added to the page?

After the above step, the View has been created, but how is the View added to the page? Let’s look at the setContentView method again

    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }
Copy the code

LayoutInflater takes two parameters. The second parameter is root, the parent view that the view is created to be added to, so the answer is clear. The view is added to the contentParent, R.I.D.C. Tent. Where did R.I.D.C tent come from?

2.1 R.id.contentWhere does it come from?

The setContentView starts with a call to the ensureSubDecor method

    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();
        }
    }
    private ViewGroup createSubDecor() {
        // Now let's make sure that the Window has installed its decor by retrieving it
        ensureWindow();
        mWindow.getDecorView();
    
        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
    
       // Omit the instantiation of the subDecor layout for other styles
       // Contains actionBar floatTitle ActionMode
       subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
       final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        // Set the ID of 'contentView' to Android.r.d.c. tent and the ID of windowContentView to view.no_id
        windowContentView.setId(View.NO_ID);
        contentView.setId(android.R.id.content);
    
        // Add subDecor to window
        mWindow.setContentView(subDecor);
        return subDecor;
    }
Copy the code

As you can see, the main job is to create subDecor and add it to the Window

  • Step 1: Confirmwindowandattach(Setting background and other operations)
  • Step 2: GetDecorView, because it is the first callinstallDecor(createDecorViewandwindowContentView)
  • Step 3: FromxmlInstantiate fromsubDecorlayout
  • Step 4: WillsubDecorthecontentViewtheidSet toR.id.content
  • Step 4: WillsubDecorAdded to thewindowIn the

Now that we know where R.D.C. Tent comes from, and that subDecor will eventually be added to Windows, the question becomes, what is a window?

2.2 windowWhat is it?

As mentioned above, the view we created will be added to subDecor and finally to Window. So what is window? Why do we have Windows?

We have multiple pages in the app, we have multiple apps on the phone, so there’s only one page on the phone at a time, Android creates a system service, WindowManagerService(WMS), to manage Windows on the screen. Views can only be displayed on the corresponding window. If it does not meet the requirements, the window will not be opened and the corresponding View will not be displayed

The Window mechanism is designed to manage the display of views on the screen and the delivery of touch events

PhoneWindow is an abstract class that has a unique implementation. Inside a PhoneWindow, there is a DecorView(root View). Its job is to do some standard processing of the DecorView, such as title, background, navigation bar, event transition, etc., which obviously does not fit the concept of window mentioned above

In general, PhoneWindow is just a standard UI solution, which is not equivalent to Windows. Windows are an abstract concept, that is, which page should be displayed. Windows are managed by WindowManagerService(WMS). Easy to understand Android view system design and implementation, write very easy to understand, interested can understand

2.3 ViewWhen is it really visible?

As mentioned above, PhoneWindow only provides some standard UI solutions and is not really a window. So when will our View be added to the window, and when will it be visible to the user?

#ActivityThread
	public void handleResumeActivity(...) {
        / /...
        / / comment 1r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); ./ / comment 2wm.addView(decor, l); . } #ViewRootImpl.javapublic void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                DecorView / / record
                mView = view;
                / / to omit
                // Open three processes of View (measure, layout, draw)
                requestLayout();
                try {
                    // Add to WindowManagerService, where the window is actually added to the underlying layer
                    // The return value here determines whether the window was successfully added, permissions, etc.
                    // If the dialog is opened with the Application context, it will add unsuccessful
                    / / comment 3
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    setFrame(mTmpFrame);
                } catch (RemoteException e) {
                }
                / / to omit
                // Input event receive}}}Copy the code
  1. Note 1 is drawn fromActivityRemove thePhoneWindow.DecorView.WindowManager
  2. Called at comment 2WindowManagertheaddViewThe method, as the name implies, is toDecorViewAdded to thewindowamong
  3. It’s going to go toViewRootImplAt note 3, this is the real passWMSOpen up a window on the screen, and at this point ourViewAnd you can display it on the screen

As you can see, when we start an Activity, the interface is really visible after onResume

2.4 Activity.PhoneWindow.ViewThe relationship between

  1. PhonewindowactivityA member variable of theActivity.attatchWhen the initialization
  2. PhoneWindowisViewStudent: Container, rightDecorViewDo some standard processing, such as title, background, navigation bar, event relay, etc
  3. ActivityprovideswindowThe lifecycle of Windows is shielded from the intricate details of the windowing mechanism, and developers only need to develop based on the template approach

As shown in the figure below

2.5 summary

The View is added to the page through several processes

  • 1. Start theactivity
  • 2. To createPhoneWindow
  • 3. Set the layoutsetContentViewThat will belayoutIdintoView
  • 4. ConfirmsubDecorViewThe initialization of thesubDecorViewAdded to thePhoneWindowIn the
  • 5. AddlayoutIdAfter the transformation of theViewtoandroid.R.id.contenton
  • In 6.onResumeLt.DecorViewViewAdded to theWindowManagerIn the
  • 7.ViewIt actually shows up on the screen

3. ViewHow exactly is it drawn?

After the previous step, the View has been added to the window, and the next step is the drawing of the View itself. The drawing of the View mainly goes through the following steps: 1. 3, confirm the placement position, you need to determine what to display above (draw)

Several stages, this View has been encapsulated template method to us, we direct rewriting onMeasure onLayout, ontouch this several methods And draw the entrance, is above ViewRootImpl. SetView requestLayout

3.1 requestLayoutHow to trigger drawing

RequestLayout triggers drawing. Let’s take a look at the source code

ViewRootImpl.java
    public void requestLayout() {
        if(! mHandlingLayoutInLayoutRequest) {// Check if it is the main thread. If not, throw an exception and generate a main thread reference when ViewRootImpl is created
            // Compare the current thread with the reference, or the main thread if the reference is the same
            // This is why the View is updated and drawn in a child thread
            checkThread();
            // Use it to indicate that a layout is needed
            mLayoutRequested = true;
            // Draw the request
            scheduleTraversals();
        }
    }
    
    void scheduleTraversals() {
        if(! mTraversalScheduled) {// Mark a draw request to block repeated requests within a short period of time
            mTraversalScheduled = true;
            // Put synchronous barrier messages into the main thread Looper queue to control the execution of asynchronous messages
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // Put into the mChoreographer queue
            // mTraversalRunnable is queued
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            / / to omit}}final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

    void doTraversal() {
        // Start drawing without canceling the drawing
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            // Remove the synchronization barrier
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            // Start implementing measure, layout, draw, etcperformTraversals(); }}Copy the code

RequestLayout basically does the following things

  1. Check the drawing thread withViewWhether the created thread is the same thread
  2. throughHandlerSynchronous barrier mechanism, guaranteedUIThe draw message has the highest priority
  3. willmTraversalRunnableThe incomingChoreographerTo monitorvsyncThe signal.
  4. receivedvsyncThe signal will be called backTraversalRunnableRemove the synchronization barrier and start the real thingmeasure.layout.draw

ViewDrawing flow chart is as follows:

3.2 MeasureSpecAnalysis of the

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec Its high 2 bits represent the measurement mode, and its low 30 bits represent the size. There are 3 measurement modes in total.

  • UNSPECIFIED: No specified mode does not matchViewThe size is limited.
  • AT_MOST: Maximum mode corresponds towrap_contentProperty that the parent container has identified childrenViewThe size of, and sonViewIt can’t be greater than that.
  • EXACTLY: Exact mode corresponds tomatch_parentProperties and concrete values, childrenViewValues that can reach the size specified by the parent container.

ordinaryviewtheMeasureSpecCreate rules as follows

With this table, we can look at a problem together

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/red"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/blue"/>
</FrameLayout>
Copy the code

What is the final color of such a layout? The answer is blue, and it fills the screen

In simple terms, when we customize a View, if we do not set MODE to wrap_content and match_content, the result is the same. The width and height of the View are the same as the width and height of the parent View

3.3 summary

  1. ViewThe drawing needs three steps of positioning, measurement and drawing in order to simplify the customizationViewThe official template method has been provided, we can rewrite the relevant method
  2. ViewRootImplIn therequestLayoutIt’s the entrance to the drawing. Of course we areViewIn the callinvalidateorrequestLayoutIt also triggers a redraw
  3. The drawing process is also by natureHandlerSends a message. In order to increase the priority of drawing messages, synchronization masking is enabled
  4. willmTraversalRunnableThe incomingChoreographerTo monitorvsyncThe signal. Pay attention to,vsyncThe signal doesn’t listen until it’s registered.
  5. receivedvsyncThe signal will be called backTraversalRunnableRemove the synchronization barrier and start the real thingmeasure.layout.drawprocess
  6. The next step is to call back eachViewtheonMeasure.onLayout.onDrawprocess

4 ViewHow does it show up on the screen after it’s drawn?

So far we’ve known sinceXMLTo the callingView.onDrawBut fromonDrawThere seems to be some distance between the display and the screen

We know that,ViewAnd then finally on the screen,CPUCalculate the frame data and hand over the calculated dataGPU.GPUThe graphics data will be rendered, rendered and placedbufferIn the image buffer, and thenDisplay(screen or monitor) in charge ofbufferThe data is presented on the screen

So the question is,canvas.drawHow is it converted toGraphic Buffer?

Its general process is shown in the figure:



As you can see, this process is quite complicated, and I won’t expand it here for space reasons. Interested students can refer to uncle Xanthii’s series of articles:Android Graphics Overview (Dry Article)

conclusion

Going from XML to View display to screen involves the following

  1. ActivityThe launch of the
  2. LayoutInflaterfillViewThe principle of
  3. PhoneWindow.Activity.ViewThe relationship between
  4. AndroidWindow mechanism andWindowManagerServiceManagement window
  5. ViewThe drawing process of,measure.layout.drawAnd such asHandlerSynchronous barrier mechanism
  6. AndroidScreen refresh mechanism,VSyncSignal monitoring, tertiary buffering, etc
  7. AndroidGraphics rendering, includingSurfaceFingerWorkflow, software drawing, hardware acceleration, etc

This article has are long, but to fully understand the process from the XML to display on the screen, or detailed enough, there are many places only briefly, if there is anything wrong or need to complement, welcome to put forward in the comments section Due to the space, there are some knowledge didn’t write very detailed, for reference some better articles listed below: Series: Android Screen Refresh mechanism – VSync, Choreographer Complete Understanding! Handler synchronization barriers: What you may not know about Handler synchronization barriers

The resources

【Android Advanced 】 This time the View drawing process engraved in mind!! The Android Activity creates the display process to a View