Understanding and Optimizing Android Activity Startup Performance
Android activity startup latency stems from three phases—Pause, Launch, and Render—so optimizing only onCreate often leaves perceived delays; developers can measure each phase via system logs or AOP hooks (Instrumentation, Looper‑Printer, or ActivityThread handler) to pinpoint and reduce bottlenecks.
Activity startup speed is a common concern for Android developers. When the transition between pages takes too long, the app feels sluggish. The first instinct is to blame a slow onCreate method, but even after optimizing onCreate the perceived delay may remain. The real cause lies in the whole Activity launch process, which can be divided into three stages: Pause , Launch and Render .
1. Activity launch flow
The launch consists of three steps (using ActivityA → ActivityB as an example):
From ActivityA.startActivity to the moment ActivityA is successfully paused.
From ActivityB's initialization to the end of its onResume method.
From ActivityB registering its window with the WindowManagerService (WMS) to the completion of the first frame rendering.
The communication between the app process, ActivityManagerService (AMS) and WindowManagerService (WMS) is complex; the article lists the key method call chains without reproducing the entire source.
2. Pause flow
When ActivityA calls startActivity , the system first pauses the currently visible Activity. The call chain is:
ActivityA.startActivity -> Instrumentation.execStartActivity -> ActivityManagerNative.getDefault.startActivity -> ActivityManagerService.startActivityAsUser -> ActivityStarter.startActivityMayWait -> ActivityStarter.startActivityLocked -> ActivityStarter.startActivityUnchecked -> ActivityStackSupervisor.resumeFocusedStackTopActivityLocked -> ActivityStack.resumeTopActivityUncheckedLocked -> ActivityStack.resumeTopActivityInnerLocked -> ActivityStack.startPausingLocked -> ActivityThread$ApplicationThread.schedulePauseActivity -> ActivityThread.handlePauseActivity -> ActivityA.onPauseActivityManagerNative.getDefault().activityPausedIf a time‑consuming operation is performed inside onPause , it directly delays the AMS notification that the previous Activity has paused, which in turn blocks the subsequent launch.
3. Launch flow
After the pause, AMS proceeds with the launch. The relevant call chain includes:
ActivityManagerService.activityPaused -> ActivityStack.activityPausedLocked -> ActivityStack.completePauseLocked -> ActivityStackSupervisor.resumeFocusedStackTopActivityLocked -> ActivityStackSupervisor.startSpecificActivityLocked -> (new process creation if needed) -> ActivityThread$ApplicationThread.scheduleLaunchActivity -> Activity.handleLaunchActivity -> Activity.onCreate -> Activity.onRestoreInstanceState -> Activity.handleResumeActivity -> Activity.onStart -> Activity.onResume -> WindowManager.addViewThe total time measured by the system (displayed in Logcat as Displayed …: +XXms ) corresponds to the Launch and Render stages. The system does not count the Pause time unless it exceeds the 500 ms timeout, after which AMS forces the pause to complete.
4. Render flow
When onResume finishes, the UI rendering begins. The rendering chain is:
WindowManager.addView -> WindowManagerImpl.addView -> ViewRootImpl.setView -> ViewRootImpl.requestLayout -> ViewRootImpl.scheduleTraversals -> Choreographer.postCallback -> ViewRootImpl.doTraversal -> ViewRootImpl.performTraversals -> ViewRootImpl.performMeasure -> ViewRootImpl.performLayout -> ViewRootImpl.performDrawThe first frame is drawn after ViewRootImpl.performDraw . Any heavy work in measure , layout or draw of child views will increase the Render time.
5. Timing collection methods
Two main approaches are described:
System statistics : Use Logcat with the ActivityManager tag to see the total launch time reported by AMS.
In‑app statistics : Implement AOP‑style hooks to measure the three stages individually.
6. In‑app measurement solutions
Three hook techniques are recommended:
Hook Instrumentation : Replace the system Instrumentation instance via reflection and override lifecycle callbacks (e.g., callActivityOnPause , callActivityOnResume ) to record timestamps.
Hook Looper‑Printer : Set a custom Printer on the main looper to log message dispatches and calculate the duration of lifecycle messages. This method does not work on Android P+ because the message codes changed.
Hook ActivityThread$H : Replace the Handler.Callback of the internal ActivityThread$H handler to intercept lifecycle messages, handling the new EXECUTE_TRANSACTION code on Android P and later.
All three methods allow developers to obtain the Pause, Launch and Render times without modifying each Activity manually.
7. Summary
Activity startup consists of Pause, Launch and Render stages.
System Logcat shows Launch + Render time; Pause is ignored unless it exceeds the 500 ms timeout.
Hook‑based AOP solutions (Instrumentation, Looper‑Printer, Handler) enable fine‑grained timing for each stage, though Looper‑Printer is not compatible with Android P+.
For further reading, the article lists several open‑source projects and blog posts related to Activity launch analysis and performance monitoring.
Didi Tech
Official Didi technology account
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.