Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Android Vitals - Rising to the first drawn surface 🤽‍♂️
Py ⚔
Py ⚔

Posted on

     

Android Vitals - Rising to the first drawn surface 🤽‍♂️

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:

  1. Creating the app object.
  2. Launching the main thread.
  3. Creating the main activity.
  4. Inflating views.
  5. Laying out the screen.
  6. 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.

Next stages

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:

Main thread start

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;}}
Enter fullscreen modeExit fullscreen mode

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 */)}}}...}}
Enter fullscreen modeExit fullscreen mode

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 theandResume 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);...}}
Enter fullscreen modeExit fullscreen mode

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);}}
Enter fullscreen modeExit fullscreen mode

WhenEXECUTE_TRANSACTION is processed it callsTransactionExecutor.execute().

We can now update the initial sequence diagram:

Main thread start - continued

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;}}}}
Enter fullscreen modeExit fullscreen mode

Activity resume

First draw

Let's look at the sequence of calls that leads to the first draw fromActivityThread.handleResumeActivity():

Choreographer.scheduleFrameLocked() enqueues aMSG_DO_FRAME message on the main thread message queue:

Scheduling MSG_DO_FRAME

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):

First draw

Conclusion

We started with a high level understanding of what happens once the system creates the app process:

Next stages

Now we know exactly what happens:

App start to first draw

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 🔎 😅:

Full picture

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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
androiddeveloperlb profile image
AndroidDeveloperLB
  • Joined

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

CollapseExpand
 
pyricau profile image
Py ⚔
Author of LeakCanary🍷🥖⛷🇫🇷
  • Location
    San Francisco
  • Work
    Android Tech Lead at Square, Inc.
  • Joined
• Edited on• Edited

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.

CollapseExpand
 
androiddeveloperlb profile image
AndroidDeveloperLB
  • Joined

So, the diagram doesn't seem right, and that's the first thing I read...

Thread Thread
 
pyricau profile image
Py ⚔
Author of LeakCanary🍷🥖⛷🇫🇷
  • Location
    San Francisco
  • Work
    Android Tech Lead at Square, Inc.
  • Joined

I was trying to show what the doc says vs what's actually happening. I guess I could have added a "please read the words above and below this diagram" header 😉

Thread Thread
 
androiddeveloperlb profile image
AndroidDeveloperLB
  • Joined

Oh sorry for that

Thread Thread
 
androiddeveloperlb profile image
AndroidDeveloperLB
  • Joined

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

Thread Thread
 
pyricau profile image
Py ⚔
Author of LeakCanary🍷🥖⛷🇫🇷
  • Location
    San Francisco
  • Work
    Android Tech Lead at Square, Inc.
  • Joined

The link is right there in the blog post :)developer.android.com/topic/perfor...

Thread Thread
 
androiddeveloperlb profile image
AndroidDeveloperLB
  • Joined

ok

Thread Thread
 
androiddeveloperlb profile image
AndroidDeveloperLB
  • Joined

I think I know why I missed it. I use an addon to make all websites in dark, and thus the link text looks exactly like normal one, so I didn't notice there is a link....

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Author of LeakCanary🍷🥖⛷🇫🇷
  • Location
    San Francisco
  • Work
    Android Tech Lead at Square, Inc.
  • Joined

More fromPy ⚔

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp