
Header image:Flying in the Lightby Romain Guy.
This blog series is focused on stability and performance monitoring of Android apps in production. Last week, I wrote about thebeginning of a cold start, from tapping a launcher icon to the creation of the app process.
TheApp startup time documentation outlines the next stages:
As soon as the system creates the app process, the app process is responsible for the next stages:
- Creating the app object.
- Launching the main thread.
- Creating the main activity.
- Inflating views.
- Laying out the screen.
- Performing the initial draw.
Once the app process has completed the first draw, the system process swaps out the currently displayed background window, replacing it with the main activity. At this point, the user can start using the app.
Diagram created withWebSequenceDiagram.
This post is a continuation of our deep dive on cold start, where we'll rise from the launch of the first activity to the first draw on a surface.
Main thread start
In theprevious blog post, we learnt that:
- When the app process starts, it callsActivityThread.main() which makes ablocking IPC call toActivityManagerService.attachApplication() in the
system_server
process. - The
system_server
process makes an IPC call toActivityThread.bindApplication() in the app process, which enqueues aBIND_APPLICATION
message to the main thread message queue. - When the IPC call toActivityManagerService.attachApplication() is done,ActivityThread.main() then callsLooper.loop() which loops forever, processing messages posted to itsMessageQueue.
- The first message processed is
BIND_APPLICATION
, which callsActivityThread.handleBindApplication() which loads the APK and loads the app components.
An important take away here is thatnothing happens in the app process main thread until the IPC call toActivityManagerService.attachApplication() returns.
Scheduling activity launch
Let's look at what happens in thesystem_server
process after callingActivityThread.bindApplication():
publicclassActivityManagerServiceextendsIActivityManager.Stub{privatebooleanattachApplicationLocked(IApplicationThreadthread,intpid,intcallingUid,longstartSeq){thread.bindApplication(...);// See if the top visible activity is waiting to run// in this process...mAtmInternal.attachApplication(...);// Find any services that should be running in this process...mServices.attachApplicationLocked(app,processName);// Check if a next-broadcast receiver is in this process...if(isPendingBroadcastProcessLocked(pid)){sendPendingBroadcastsLocked(app);}returntrue;}}
The line relevant to activity launch ismAtmInternal.attachApplication(...)
. It callsActivityTaskManagerService.attachApplication() which callsRootActivityContainer.attachApplication:
classRootActivityContainerextendsConfigurationContainer{booleanattachApplication(WindowProcessControllerapp){for(ActivityDisplaydisplay:mActivityDisplays){ActivityStackstack=display.getFocusedStack()ActivityRecordtop=stack.topRunningActivityLocked();stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);for(ActivityRecordactivity:mTmpActivityList){if(activity.app==null&&app.mUid==activity.info.applicationInfo.uid&&app.mName.equals(activity.processName)){mStackSupervisor.realStartActivityLocked(activity,app,top==activity/* andResume */,true/* checkConfig */)}}}...}}
The above code:
- Iterates over each display.
- Retrieves the focused activity stack for that display.
- Iterates on each activity of the focused activity stack.
- CallsActivityStackSupervisor.realStartActivityLocked() if that activity belongs to the process being started. Notice the
andResume
parameter passed as true if the activity is at the top of the stack.
Here'sActivityStackSupervisor.realStartActivityLocked():
publicclassActivityStackSupervisor{booleanrealStartActivityLocked(ActivityRecordr,WindowProcessControllerproc,booleanandResume,booleancheckConfig){...ClientTransactionclientTransaction=ClientTransaction.obtain(proc.getThread(),r.appToken);clientTransaction.addCallback(LaunchActivityItem.obtain(...));// Set desired final state.finalActivityLifecycleItemlifecycleItem;if(andResume){booleanforward=dc.isNextTransitionForward()lifecycleItem=ResumeActivityItem.obtain(forward);}else{lifecycleItem=PauseActivityItem.obtain();}clientTransaction.setLifecycleStateRequest(lifecycleItem);// Schedule transaction.mService.getLifecycleManager().scheduleTransaction(clientTransaction);...}}
All the method calls we've looked at so far are happening in thesystem_server
process.ClientLifecycleManager.scheduleTransaction() makes an IPC call toActivityThread.scheduleTransaction() in theapp process, which callsClientTransactionHandler.scheduleTransaction() to enqueue aEXECUTE_TRANSACTION
message on the main thread message queue:
publicabstractclassClientTransactionHandler{/** Prepare and schedule transaction for execution. */voidscheduleTransaction(ClientTransactiontransaction){transaction.preExecute(this);sendMessage(ActivityThread.H.EXECUTE_TRANSACTION,transaction);}}
WhenEXECUTE_TRANSACTION
is processed it callsTransactionExecutor.execute().
We can now update the initial sequence diagram:
Actual activity launch
TransactionExecutor.execute() callsTransactionExecutor.performLifecycleSequence() which calls back into ActivityThread tocreate,start andresume the activity:
publicclassTransactionExecutor{privatevoidperformLifecycleSequence(...){for(inti=0,state;i<path.size();i++){state=path.get(i);switch(state){caseON_CREATE:mTransactionHandler.handleLaunchActivity(...);break;caseON_START:mTransactionHandler.handleStartActivity(...);break;caseON_RESUME:mTransactionHandler.handleResumeActivity(...);break;caseON_PAUSE:mTransactionHandler.handlePauseActivity(...);break;caseON_STOP:mTransactionHandler.handleStopActivity(...);break;caseON_DESTROY:mTransactionHandler.handleDestroyActivity(...);break;caseON_RESTART:mTransactionHandler.performRestartActivity(...);break;}}}}
First draw
Let's look at the sequence of calls that leads to the first draw fromActivityThread.handleResumeActivity():
- ActivityThread.handleResumeActivity()
- ➡️WindowManagerImpl.addView()
- ➡️WindowManagerGlobal.addView()
- ➡️ViewRootImpl.setView()
- ➡️ViewRootImpl.requestLayout()
- ➡️ViewRootImpl.scheduleTraversals()
- ➡️Choreographer.postCallback()
- ➡️Choreographer.scheduleFrameLocked()
Choreographer.scheduleFrameLocked() enqueues aMSG_DO_FRAME
message on the main thread message queue:
WhenMSG_DO_FRAME
is processed it callsChoreographer.doFrame() which callsViewRootImpl.doTraversal() which performs ameasure pass, alayout pass, and finallythe first draw pass on the view hierarchy (seeHow Android Draws Views):
Conclusion
We started with a high level understanding of what happens once the system creates the app process:
Now we know exactly what happens:
Let's put this together with what we learnt from the prior blog post, and look at everything that happens from whenthe user taps a launcher icon to when the first activity is drawn andthe user can start using the app. You might need to zoom in 🔎 😅:
Now that we have the full picture, we can start digging into how to properly monitor cold start. Stay tuned for more!
Top comments(9)

Isn't "creating the main thread" before "creating the app object" ?
That's how some dependencies start before the app, no?

Absolutely, the main thread is the first thread created so that happens before the Application instance is created. I'm not certain what the Android documentation is trying to convey here, it's likely wrong or maybe to very clear.

Can you please show the original of those mistakes?
Maybe you should report it to Google:
issuetracker.google.com/issues

The link is right there in the blog post :)developer.android.com/topic/perfor...
For further actions, you may consider blocking this person and/orreporting abuse