Display process from Android Activity creation to View

Keywords: Java Android SQLite

preface

Series of articles:

Display process from Android Activity creation to View
Android four components communication core

When we click the desktop icon to start the App, and the App is displayed, what has happened? Understanding this part will deepen our impression of the relationship and difference among Activity, Window and View, and better guide us to write code.
Through this article, you will learn:

1. The Activity is created to onCreate() and onResume methods for execution
2. Application creation to onCreate method execution
3. Activity lifecycle listening
4. Window/WindowManager create and contact
5. WindowManager addView procedure
6. Three processes of ViewRootImpl creation and View
7. Android screen refresh signal simple understanding

Android Main method

Anyone who has written JavaSE applications or other applications knows that the entry of a program is the main() method. Where is the main() method of Android?
ActivityThread.java

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

When the process is fork ed out, execute the main() method. The method is short and briefly lists the areas of concern:

  • Construct an ActivityThread instance
  • Open the Looper loop (the main thread is the Looper of the UI thread)

Looper is the core of Android message (event) driven, and the steps of interest are: Android event driven handler message looper parsing

Application creation process

Take a look at the ActivityThread.attach() method:

   @UnsupportedAppUsage
    private void attach(boolean system, long startSeq) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());

            //mgr is the ActivityManagerService instance
            final IActivityManager mgr = ActivityManager.getService();
            try {
                //mAppThread is ApplicationThread
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }

            //ellipsis
        }
    }

ActivityManagerService.java method

    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            
            //Pass the iaapplicationthread thread object to AMS, which transmits data to the current process through the thread
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }

In attachApplicationLocked(thread, callingPid, callingUid, startSeq), pay attention to two points:

1. bindApplication method of ActivityThread
2,mAtmInternal.attachApplication(app.getWindowProcessController()):

Let's start with the first point:

    public final void bindApplication(String processName, ApplicationInfo appInfo,
                                      List<ProviderInfo> providers, ComponentName instrumentationName,
                                      ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                                      IInstrumentationWatcher instrumentationWatcher,
                                      IUiAutomationConnection instrumentationUiConnection, int debugMode,
                                      boolean enableBinderTracking, boolean trackAllocation,
                                      boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                                      CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                                      String buildSerial, AutofillOptions autofillOptions,
                                      ContentCaptureOptions contentCaptureOptions) {
        //ellipsis
        AppBindData data = new AppBindData();
        //ellipsis
        //Switch to main thread
        sendMessage(H.BIND_APPLICATION, data);
    }

Then, after receiving H.BIND_APPLICATION information in handleMessage(), we call handleBindApplication(data), which focuses on the following points:

    private void handleBindApplication(AppBindData data) {
        //ellipsis
        if (ii != null) {
            //ellipsis
        } else {
            //Create an Instrumentation object, and the ActivityThread holds the object reference
            //The creation of Application and Activity requires calling the relevant methods of instrumentation
            mInstrumentation = new Instrumentation();
            mInstrumentation.basicInit(this);
        }
        //ellipsis
        Application app;
        try {
            //A
            //info is the LoadedApk type. As the name suggests, it stores some information of the apk package, such as application name, data storage directory, etc
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            
            //ellipsis
            try {
                //B
                //After the app is created, call the method notification
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
               //ellipsis
            }
        } finally {
            //ellipsis
        }
    }

Continue to analyze points A and B in the above code comments.
Let's look at A first
LoadedApk.makeApplication() method:

    public Application makeApplication(boolean forceDefaultAppClass,
                                       Instrumentation instrumentation) {
        //Instrumentation passed = null
        //app has been created, no need to create
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        String appClass = mApplicationInfo.className;
        //ellipsis
        try {
            //Get ClassLoader 
            java.lang.ClassLoader cl = getClassLoader();
            //ellipsis
            
            //Create the context of the app. Note that it is not the app itself
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            
            //Creating an app through Instrumentation
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            
            appContext.setOuterContext(app);
        } catch (Exception e) {
            //ellipsis
        }
        
        //assignment
        mApplication = app;
        //ellipsis
        return app;
    }

The Application object was created and handed over to the Instrumentation.newApplication method:

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        //Creating an Application object through reflection calls the default constructor
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        
        //Assign the ContextImpl object to the mBase reference of the Application
        app.attach(context);
        return app;
    }

Take another look at B

    public void callApplicationOnCreate(Application app) {
        //In fact, it calls the onCreate method of Application to notify the App that the creation is complete
        app.onCreate();
    }

Let's now create the Application subclass App

public class App extends Application {
    public App() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

Through the above analysis, we can know the call timing of App() and onCreate(). So far, the analysis of Application creation process is completed.

Activity creation process

After the Application is created, it's time to start the Activity, which is known as "MainActivity". The second point in attachApplicationLocked(): mAtmInternal.attachApplication(app.getWindowProcessController());
mAtmInternal is an ActivityTaskManagerService type

        @Override
        public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
            synchronized (mGlobalLockWithoutBoost) {
                return mRootActivityContainer.attachApplication(wpc);
            }
        }

ActivityStackSupervisor.java

    boolean attachApplication(WindowProcessController app) throws RemoteException {
        final String processName = app.mName;
        //ellipsis
        final int size = mTmpActivityList.size();
        for (int i = 0; i < size; i++) {
            final ActivityRecord activity = mTmpActivityList.get(i);
            if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
                    && processName.equals(activity.processName)) {
                try {
                    //Start activity
                    if (mStackSupervisor.realStartActivityLocked(activity, app,
                            top == activity /* andResume */, true /* checkConfig */)) {
                        didSomething = true;
                    }
                } catch (RemoteException e) {
                }
            }
        }

        //ellipsis
        return didSomething;
    }

Then call: mservice. Getlifecycle manager(). Scheduletransaction (clienttransaction);
The LaunchActivityItem is passed to the ActivityThread, returning to the familiar place. Finally, the handleLaunchActivity() method in the ActivityThread is called:

    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
                                         PendingTransactionActions pendingActions, Intent customIntent) {
        //ellipsis
        //Initialize WindowManagerService, which will be used to add a window later
        WindowManagerGlobal.initialize();
        final Activity a = performLaunchActivity(r, customIntent);
        //ellipsis
        return a;
    }

Focus on performLaunchActivity.

    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        //ellipsis

        //Create a context of type ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            //Like the App creation process, Instrumentation constructs Activity instances through reflection
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        } catch (Exception e) {
        }

        try {
            //Create an app, because it has been created before, so the Application object is directly returned here
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            
            //ellipsis

            if (activity != null) {
                Window window = null;
                appContext.setOuterContext(activity);
                //Key method A
                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);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }

                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    //set up themes
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    //Notify the Activity that it has been created successfully. Focus on method B
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                
                //Record activity
                r.activity = activity;
            }

        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
        }

        return activity;
    }

Key method A
After the Activity object is created, some properties need to be associated, which is actually initializing its member variables, such as Context, Window, WindowManager, Application, etc.

    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) {
        //Associated mBase
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        //Create a window. Window is an abstract class. Here, create its subclass PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        //Some callback interfaces of window, such as touch / physical key event distribution, focus change, attachment / de attachment to window, etc
        mWindow.setCallback(this);
        //The ui thread is the current thread
        mUiThread = Thread.currentThread();
        //ActivityThread
        mMainThread = aThread;
        //Reference Instrumentation
        mInstrumentation = instr;
        //Record token
        mToken = token;
        //Record Application
        mApplication = application;
        //title
        mTitle = title;
        
        //Create a windowManager as a member variable of Window
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        
        //Activity WindowManager variable assignment
        mWindowManager = mWindow.getWindowManager();
        
        //Note: some parts of the above are omitted
    }

Key method B
Similar to the Application, after the Activity is created, notify the Activity to call back its onCreate method. This action is initiated by Instrumentation.

ActivityThread.java
    public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }
   Activity.java
    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        //onCreate method and subclasses in Activity will override this method
        if (persistentState != null) {
            onCreate(icicle, persistentState);
        } else {
            onCreate(icicle);
        }
    }

    @CallSuper
    protected void onCreate(@android.annotation.Nullable Bundle savedInstanceState) {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
        //ellipsis
        //Activity distribution creation event
        dispatchActivityCreated(savedInstanceState);
        //ellipsis
    }

    private void dispatchActivityCreated(@android.annotation.Nullable Bundle savedInstanceState) {
        //Callback collection in Application
        getApplication().dispatchActivityCreated(this, savedInstanceState);
        
        //Return to collection in Activity
        Object[] callbacks = collectActivityLifecycleCallbacks();
        
        //The collection here is of ActivityLifecycleCallbacks type. We usually want to listen to the Activity life cycle through
        //registerActivityLifecycleCallbacks() method is available in both Application and Activity
        if (callbacks != null) {
            for (int i = 0; i < callbacks.length; i++) {
                //Notifies the observer that the Activity onCreate() method has been executed
                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
                        savedInstanceState);
            }
        }
    }

At this point, the Application and Activity have been created. You may wonder when the View will be displayed?

View creation process

When creating an Activity, it will bind layout and start analysis from here (take the example of inheriting from AppCompatActivity here. It is simpler if it inherits directly from Activity).

Activity.java
    protected void onCreate(@Nullable Bundle savedInstanceState) {
         //Assign the Activity window variable to AppCompatDelegateImpl
        //mWindow member variable
        //AppCompatDelegateImpl is created when Activity.attach()
        super.onCreate(savedInstanceState);
        //Binding layout
        setContentView(R.layout.activity_main);
    }

    public void setContentView(@LayoutRes int layoutResID) {
        //The associated layout is finally connected to the ViewTree structure
        getDelegate().setContentView(layoutResID);
    }

Take a look at AppCompatDelegateImpl.java

    public void setContentView(int resId) {
        /**
         * 1,Create a DecorView and assign AppCompatDelegateImpl mWindow to the DecorView mWindow variable
         * 2,PhoneWindow mDecor The reference holds the DecorView object, that is, DecorView and PhoneWindow hold each other
         * other party
         *
         */
        ensureSubDecor();

        //Take out the child view named content in DecorView and remove it
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();

        //Loading a custom layout is actually the process of building a View tree, usually ViewGroup
        //Add the customized layout as a child view to the contentParent
        //So far, DecorView has our own customized content
        LayoutInflater.from(mContext).inflate(resId, contentParent);
    }

At this point, the entire View tree is created, that is, the View tree is also created when the Activity is created. Although Window and DecorView are simply associated (holding references to each other), they do not seem to see any special value. When will View be added to Window?

WindowManager addView procedure

As mentioned earlier, the Activity life cycle is managed by AMS. We analyzed that the Activity creation notification is sent to the ActivityThread by AMS. What does the ActivityThread do when the Activity start s and resume s?
Take a look at handleResumeActivity():

ActivityThread.java
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
                                     String reason) {
        //Final call to Activity onResume()
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

        if (r.window == null && !a.mFinished && willBeVisible) {
            //Take out the window in the Activity, which is associated when the window is created
            r.window = r.activity.getWindow();
            //Take out the DecorView in the window, which is associated with Activity.setContentView(), when the DecorView is created
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            //It is also when the Activity is created that the windowManager on the association inherits the ViewManager interface
            //Windows manager itself is also an interface
            ViewManager wm = a.getWindowManager();
            //The layout attribute of the window is generated when the window is created. The default width and height fill the parent window
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            //Window window type
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            //ellipsis
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //Key method a: add DecorView to WindowManager
                    wm.addView(decor, l);
                } else {
                }
            }
        }
        //ellipsis
    }

Key method A
wm.addView(decor, l). addView is the method in the ViewManager interface:

    public interface ViewManager
    {
        //Add View
        public void addView(View view, ViewGroup.LayoutParams params);
        //Update View
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);
        //Remove View
        public void removeView(View view);
    }

WindowManager is an interface, so what is its implementation class? The answer is when creating WindowManager:

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

As you can see, WindowManagerImpl is the implementation class of WindowManager. Let's take a look at its addView()

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //It is handled by WindowManagerGlobal. WindowManagerGlobal is a class that provides singleton access
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
                        Display display, Window parentWindow) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        //ellipsis
        //ViewRootImpl
        ViewRootImpl root;
        
        View panelParentView = null;
        synchronized (mLock) {
            //Create ViewRootImpl object
            root = new ViewRootImpl(view.getContext(), display);
            //The View passed in here is DecorView. Set the layoutParam of DecorView and fill the parent control by default
            view.setLayoutParams(wparams);

            //WindowManagerGlobal is shared globally
            //There may be multiple decorviews (for each Activity, Dialog, etc.)
            //Therefore, you need a List to record relevant information
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            
            try {
                //Call the ViewRootImpl setView method
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
            }
        }
    }

WindowManagerGlobal addView finally called the setView method of ViewRootImpl:
This method is relatively long, and the key explanation is selected:

ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                //Record DecorView
                mView = view;
                //ellipsis
                //Open the three processes of View (measure, layout and draw)
                requestLayout();
                try {
                    //Add to WindowManagerService. Here is the real bottom layer of window
                    //The return value here determines whether the window is successfully added, permission judgment, etc.
                    //For example, if you use the context of the Application to open the dialog, it will not be added successfully
                    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) {
                }
                
                if (mInputChannel != null) {
                    //Register event listening and distribute it to the native layer when the touch event / physical key event arrives 
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

                //Set ViewParent mParent;
                //This variable records the parent view of each view, such as invalidate and requestLayout of the view
                //It is used to traverse the view tree layer by layer
                //It is recorded here that the Parent of DecorView is ViewRootImpl
                view.assignParent(this);
                
                //ellipsis
                //Input event reception
            }
        }
    }

Through mWindowSession.addToDisplay(), we know that Window has been linked to WindowManagerService. The rest is the measure, layout and draw processes of View itself. In fact, the entry is requestLayout().

Three processes drawn by View

From construction to display, a View needs to go through the following steps:

1. Create View object (construct)
2. Determine the space occupied by the View (measure)
3. Once the space size is determined, it is necessary to determine the layout
4. After confirming the placement position, you need to determine what to draw on it

requestLayout

The following content relates to Looper. If you have questions, you can read this article first: Android event driven handler message looper parsing

ViewRootImpl.java
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            //Check whether it is the main thread. If not, throw an exception directly, and generate a main thread reference when ViewRootImpl is created
            //Compare the current thread with the reference. If it is the same, it is the main thread
            //This is also the reason why an error will be reported when the child thread updates and draws the View
            checkThread();
            //Used to mark the need for layout
            mLayoutRequested = true;
            //Draw request
            scheduleTraversals();
        }
    }
    
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            //Mark a drawing request to mask repeated requests in a short time
            mTraversalScheduled = true;
            //Put synchronous barrier messages into the Looper queue of the main thread to control the execution of asynchronous messages
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //Put it in the mchorographer queue
            //It mainly puts mTraversalRunnable into the queue
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            //ellipsis
        }
    }

The Choreographer class is introduced here. This class is created during the construction of ViewRootImpl and obtained through ThreadLocal

Choreographer.java
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
                @Override
                protected Choreographer initialValue() {
                    //ViewRootImpl is constructed in the main thread, where the looper of the main thread is obtained
                    Looper looper = Looper.myLooper();
                    //Constructing Choreographer objects
                    Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
                    return choreographer;
                }
            };

    private Choreographer(Looper looper, int vsyncSource) {
        //Record looper
        mLooper = looper;
        //Define the message that the Handler receives
        mHandler = new FrameHandler(looper);
        //Define the subclass DisplayEventReceiver, which is used to receive the underlying refresh signal
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;;
        //Internal queues are used to maintain various requests, such as transverse callback
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }

Put in queue:

Choreographer.java
    private void postCallbackDelayedInternal(int callbackType,
                                             Object action, Object token, long delayMillis) {
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            //Put in queue
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                //Execute now
                scheduleFrameLocked(now);
            } else {
                //Asynchronous execution
            }
        }
    }

Finally, call the DisplayEventReceiver scheduleVsync method:

DisplayEventReceiver.java
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            //The native method registers the synchronization pulse signal event and tells the bottom layer that I need to refresh the signal. Remember that your refresh time is up and send me a signal
            //The bottom layer refreshes every 16ms. If the upper layer does not register the synchronization pulse signal event, the upper layer will not be notified when the bottom layer refreshes.
            nativeScheduleVsync(mReceiverPtr);
        }
    }

Well, here, requestLayout() has been completed, and we'll wait for the underlying refresh signal.
Following the principle of receiving wherever you register, take a look at the DisplayEventReceiver class and find the dispatchVsync method:

DisplayEventReceiver.java
    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        //Overridden by FrameDisplayEventReceiver subclass of DisplayEventReceiver
        onVsync(timestampNanos, physicalDisplayId, frame);
    }

DisplayEventReceiver is an abstract class whose subclass is FrameDisplayEventReceiver

FrameDisplayEventReceiver.java
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
        //ellipsis
        //Construct a Message and use this, that is, call back its own run method
        Message msg = Message.obtain(mHandler, this);
        //Set as asynchronous message. When a barrier message is encountered, the asynchronous message will be executed first
        //Ensure that the refresh signal can be executed in time, that is, the view drawing priority is the highest
        msg.setAsynchronous(true);
        //send message
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }

    @Override
    public void run() {
        mHavePendingVsync = false;
        //Execute refresh message
        //Finally, the method execution in mCallbackQueues is taken out
        doFrame(mTimestampNanos, mFrame);
    }

When was the method taken out from the doFrame put in? It was before

mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

Therefore, the doFrame will finally call back the mTraversalRunnable run method

ViewRootImpl.java
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

    void doTraversal() {
        //If you do not cancel drawing, start drawing
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //Remove synchronization barrier
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            //Really start to execute measure, layout, draw and other methods
            performTraversals();
        }
    }

I went back to ViewRootImpl.
requestLayout is summarized as follows:

1. Add the drawing request to the queue to be executed and send a message to the bottom layer to indicate that it has content to refresh. At this time, reqeustLayout has been executed.
2. When the bottom layer is refreshed at an interval, the registration signal of the upper layer is detected, so it is sent to the upper layer to indicate that my side has been refreshed. Please change your interface quickly.
3. When receiving the bottom signal, send it to the looper queue of the main thread and mark me. This is to tell others that this is the signal of interface refresh. Don't delay. Give priority to implementation.
4. Execute the request in the first step and carry out the three drawing processes of view.

There are two problems to pay attention to. Remember some tags mentioned in the code to filter repeated requests in a short time.

1. mTraversalScheduled flag. If the drawing request is not executed before the callback, the next request will be ignored, such as the repeated requestLayout in a short time.
2. mFrameScheduled flag. If the underlying refresh signal does not come before, the signal sent to the underlying again will be ignored.

summary

At this point, the whole process from Application to Activity creation to View being added to Window has been sorted out until drawing is initiated. Briefly summarize the relationship between:

1. Activity displays content through Window
2. Window manages view trees through ViewRootImpl
3. The real content is displayed through View

Finally, a diagram is used to illustrate the whole process:

requestLayout is shown in Figure:

This article is based on Android 10.0 source code.

If you like it, please like it and pay attention to your encouragement, which is the driving force for me to move forward.

Posted by thepeccavi on Mon, 04 Oct 2021 12:33:10 -0700