Android Application Launch Sequence from Icon Tap to Process Initialization
When a user taps an app icon on the Android home screen, a multi-layered system sequence initiates to launch the application. This process differs significantly between cold and hot starts:
- Cold start: Occurs when the app has no running process. The system must create a new process, initialize the
Applicationobject, and launch the targetActivity. - Hot start: Happens when the app process already exists. The system merely brings the existing
Activityto the foreground, skipping process creation andApplicationinitialization.
The cold start path involves deep coordination between system services and native processes, beginning with the launcher and ending with the app’s main thread execution.
From Launcher to Zygote Notification
Tapping an app icon in Launcher (e.g., Launcher3) triggers ItemClickHandler.startAppShortcutOrInfoActivity(), which eventually calls startActivitySafely() in BaseActivity. This flows into the standard Activity.startActivity() → startActivityForResult() chain.
Since the launcher has no parent activity, it delegates to Instrumentation.execStartActivity(). This method communicates with the system server via Binder IPC by invoking ActivityTaskManager.getService().startActivity().
ActivityTaskManager is a client-side proxy that routes the request to ActivityTaskManagerService (ATMS) in the system server. ATMS uses ActivityStarter to validate and prepare the launch:
// Simplified flow in ActivityStarter
ActivityRecord r = new ActivityRecord.Builder(...).build();
startActivityUnchecked(r, ...);
startActivityInner(r, ...);
mRootWindowContainer.resumeFocusedTasksTopActivities(...);
RootWindowContainer pauses the current foreground task and determines that a new process is needed. It signals back to ATMS, which then calls mAmInternal.startProcess()—an internal interface implemented by ActivityManagerService (AMS).
AMS handles process creation through startProcessLocked(), which consults ProcessList to configure process parameters (UID, GID, ABI, etc.) and selects the appropriate Zygote socket (primary, WebView, or isolated).
Finally, ZygoteProcess.startViaZygote() serializes arguments and sends them over a local socket to the Zygote daemon.
Zygote Forks the Application Process
The Zygote process, started early during system boot, listens for connection requests via ZygoteServer.runSelectLoop(). Upon receiving a launch command, it spawns a ZygoteConnection to parse arguments and fork a child process:
pid = Zygote.forkAndSpecialize(uid, gid, ..., niceName);
if (pid == 0) {
// Child process
return handleChildProc(parsedArgs, ...);
} else {
// Parent (Zygote) process
handleParentProc(pid, ...);
}
In the child process, handleChildProc() invokes ZygoteInit.zygoteInit(), which sets up the runtime environment and calls RuntimeInit.findStaticMain(). This method uses reflection to locate and invoke the entry point:
// Entry class: android.app.ActivityThread
Class<?> cl = Class.forName("android.app.ActivityThread");
Method m = cl.getMethod("main", String[].class);
m.invoke(null, new Object[]{args});
Application Initialization in ActivityThread
ActivityThread.main() initializes the main looper and attaches to the system:
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false); // false = not system app
Looper.loop();
}
The attach() call registers the app with AMS and schedules a BIND_APPLICATION message via the internal H handler. Processing this message triggers handleBindApplication(), which:
- Loads the app’s
Applicationclass viaClassLoader. - Instantiates it using
Instrumentation.newApplication(). - Calls
Application.onCreate().
Only after this is complete does the system proceed to instantiate and launch the target Activity.
This end-to-end flow illustrates how Android leverages Zygote for efficient process forking, Binder for inter-process communication, and Handler/Looper for intra-process message dispatch—all foundational to the platform’s architecture.