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.