Problem 1.

1. When is the layout in XML rendered to the screen?

2. Can child threads update the UI?

2. Source code analysis

Write in front: the following source code is only part of the core part, the SDK part is Java code (based on Android-29, different versions of the code may vary, but the core logic does not change much), the handwritten part uses Kolin language.

<LinearLayout xmlns:tools="http://schemas.android.com/tools">
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    
    <androidx.appcompat.widget.AppCompatTextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>
</LinearLayout>
Copy the code
  override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)
    }
Copy the code

Although there is an AppCompatActivity from the source point, it still ends up executing the setContentView method in the Activity, so let’s look directly at the code in the Activity

//package android.app.Activity
 public void setContentView(@LayoutRes int layoutResID) {
        / /!!!!!! Here is the core code
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
    
 public Window getWindow(a) {
        return mWindow;
 }
Copy the code

Window is an abstract class, and we should find its implementation class and where it is initialized. Since There is actually only one implementation of Window in Android, we go directly to PhoneWindow.

public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } 
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            ...
        } else {
            // Add the layout passed in the activity to mContentParentmLayoutInflater.inflate(layoutResID, mContentParent); }... }private void installDecor(a) {
        if (mDecor == null) {
            // Create a DecorView object
            mDecor = generateDecor(-1); }...if (mContentParent == null) {
            / / add a system layout R.l ayout. Screen_simple into DecorView, and find the id for the com. Android. Internal. R.i, dc ontent mContentParent layout assignment
            mContentParent = generateLayout(mDecor);
        }
Copy the code

After the PhoneWindow calls the setContentView method, a DecorView object is created, a system layout is added to the DecorView, and the layout passed in by the Activity is added to it, something like this

At this point, the layout has all been added, but we know that in order to display properly on the screen, it must be measured, laid out, and drawn to render correctly to the specified location. We know that all processes start from the main method, and Android processes are no exception, so we find activityThread. Java’s main method.

public static void main(String[] args) {... Looper.prepareMainLooper(); . ActivityThread thread =new ActivityThread();
        thread.attach(false, startSeq); . Looper.loop(); }Copy the code

There will be some initialization, creating an application instance, setting up Looper, etc., but this is not the focus of this article, so I won’t explain it too much. When an Activity is started, ActivityThread#handleLaunchActivity() is executed

//ActivityThread.java
 public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {...finalActivity a = performLaunchActivity(r, customIntent); . }private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    Activity activity = null;
    java.lang.ClassLoader cl = appContext.getClassLoader();
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    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);
 }
 //Activity.java
 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,Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {...// Create the PhoneWindow object
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    // Create a WindowMananger object (actually a WindowManagerImpl object)mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0); mWindowManager = mWindow.getWindowManager(); . }Copy the code

ActivityThread#handleResumeActivity() is then executed, and the actual drawing starts here

 @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {...// The Activity's onResume method is eventually executed before the drawing has actually started
        finalActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); }...if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            // Get the DecorView object created in the phoneWindow corresponding to the activity
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            // Get the windowManage object created in the previous step
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                ViewRootImpl impl = decor.getViewRootImpl();
                if(impl ! =null) { impl.notifyChildRebuilt(); }}...// Add the Decorview to the windowManager object, which, as you can see from the previous step, is actually a windowManagerImpl object
            wm.addView(decor, l);
Copy the code

So what does Windows Manager ImpL’s addView method do

//android/view/WindowManagerImpl.java
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

//android/view/WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {... ViewRootImpl root; root =new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            // Global is used as a singleton. The following steps are used to store some data information in collections
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            / /!!!!!! Here the ViewRootImpl is associated with the DecorView
            root.setView(view, wparams, panelParentView);
    }
    
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {... mWindowAttributesChanged =true; mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED; mAttachInfo.mRootView = view; mAttachInfo.mScalingRequired = mTranslator ! =null;
                mAttachInfo.mApplicationScale =
                        mTranslator == null ? 1.0 f: mTranslator.applicationScale; ./ /!!!!!! This is where the first drawing process beginsrequestLayout(); . setFrame(mTmpFrame); }Copy the code

Now comes the familiar drawing process

//android/view/ViewRootImpl.java
    public void requestLayout(a) {
        if(! mHandlingLayoutInLayoutRequest) {// Check that the thread on which the ViewRootImpl was created is the same as the thread on which the current method was executed.
            checkThread();
            mLayoutRequested = true;
            / /!!!!!! This is where the drawing task is scheduledscheduleTraversals(); }}void scheduleTraversals(a) {
        if(! mTraversalScheduled) { mTraversalScheduled =true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            / /!!!!!! MTraversalRunnable is an asynchronous task
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run(a) { doTraversal(); }}void doTraversal(a) {... performTraversals(); . }private void performTraversals(a) {.../ /!!!!!! This is where the measurement starts (which may happen multiple times) and ends with mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ./ /!!!!!! This is where the layout begins, and eventually mView.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()) is executed;performLayout(lp, mWidth, mHeight); .if (triggerGlobalLayoutListener) {
            mAttachInfo.mRecomputeGlobalAttributes = false;
            // This listener is a good time to get the size of the viewmAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); }.../ /!!!!!! So let's draw it
        performDraw();
    }
Copy the code

3. Summary

1. When is the layout in XML rendered to the screen? Answer: Approximately after the Activity’s onResume lifecycle

2. Can child threads update the UI? A: Child threads can update the UI as long as they bypass the checkThread method in ViewRootImpl or meet the checkThread criteria

My level is limited, if there is an error, please leave a message. If this article has helped you at all, please give it a thumbs up