The Journey of Launching An Android Activity

The Journey of Launching An Android Activity

What happens in Android, stays in Android

You're an Android developer; you know very well how to launch an Android activity and by the time you are reading this you probably did it a zillion times. But have you ever wondered why it's done this way? Why do we use intents to create an activity object? What makes an activity object unique from any other Java object? After all, it’s still a Java object, right? So, where, when, and how does the new Activity() statement get executed? What happens before an activity gets created? In this article, we’ll take a behind-the-scenes look at what happens when you launch an Android activity. This discussion assumes you’re familiar with Java, object-oriented programming (OOP) principles, and Android development fundamentals.


Let’s start by creating a project with an activity and override the default constructor. The simplest way to check the path to a constructor is by placing a breakpoint on it. It will enable us to walk through all the calls and explore the implemented code to find exactly what we want. Executing the project will result in the following:

Android studio debugger

In any Unix-based operating system, the first thing the kernel does is to execute init process. Init is the root/parent of all processes executing on Linux.

As we can see at the very bottom of the debugger frames, Android internally calls something called ZygoteInit

What is ZygoteInit

As the name suggests, ZygoteInit is the genesis of the Android application. It gets its name from a dictionary definition: “The initial cell formed when a new organism is produced."

Android at its core has a process called Zygote, which starts at init. It’s a special Android OS process that enables shared code across Dalvik/Art VM. This process is a “warmed-up” process, meaning it preloads all the classes and resources that an app may potentially need at runtime into the system’s memory. It then forks itself to start a well-managed process called SystemServer which initializes all core platform services it houses. One of the core services that gets started by SystemServer, which is particularly relevant here, is ActivityManagerService.

ActivityManagerService: Where the Magic Happens

ActivityManagerService is responsible for creating new Activity thread processes, maintaining the Activity lifecycle, and managing the activity stack. During its startup, it performs three main steps:

1. Collect Target Activity Information

Full implementation: ActivityManagerService.resolveActivityInfo()

The first step is to collect information about the target activity. This is done by calling resolveIntent() method on some hidden implementation for IPackageManager interface. The target information is then saved back into the intent object to avoid redoing this step.

2. Check User Privileges

Full implementation: ActivityManagerService.grantUriPermissionLocked()

The next step is to check if the user has sufficient privileges to invoke the target activity. This is typically done by calling grantUriPermissionLocked() which ensures:

  1. The target package has a valid UID associated with it.

  2. The UID is used to check if the target package can grant permission to access the URI by calling checkGrantUriPermissionLocked()

  3. After ensuring that the caller has all the necessary permissions, grantUriPermissionUncheckedLocked() grants them to the target package.

3. Activity Object Instantiation

Now to the juicy stuff, it’s time to check if the ProcessRecord already exists for the process. If the ProcessRecord is null, the ActivityManagerService must create a new process to instantiate the activity—duh. The instantiation is done with the help of ActivityThread class.

At this point, all required permissions have been granted, all the information about the target activity has been stored in the intent object, and the system has made sure that the user has all the privileges to invoke the target activity. Now it’s time to instantiate and launch the target Activity. This phase can be divided into three sub-phases:

3.1. Get the Environment Ready

Full implementation: ActivityThread.handleLaunchActivity()

First things first, let’s get the environment ready. It does so by calling handleLaunchActivity(). This ensures that configurations are up to date, the graphics environment is initialized, and the WindowManager is set up.

3.2. Retrieve the Previously Collected Information

Full implementation: ActivityThread: performLaunchActivity()

Next, previously collected information, such as ComponentName is retrieved, and the baseContext is created to prepare for instantiation.

3.3. The Instantiation

Full implementation: AppComponentFactory.instantiateActivity()

This is where it gets interesting. Android uses the help of Java's reflection library to execute the new Activity() statement. It does so by calling newActivity() method on the Instrumentation object which then delegates the action to theAppComponentFactory object by calling instantiateActivity() method.

After instantiation,ActivityThread proceeds by calling callActivityOnCreate(). And this is the start of what we all know as Activity Life Cycle 😄

TL;DR

This article delves into the behind-the-scenes process of launching Android activities, exploring why intents are used and what makes an Activity object unique. It covers the role of the Zygote process, the initialization of core services by SystemServer, and the crucial functions of ActivityManagerService. The article explains how the system collects target activity information, checks user privileges, and instantiates the Activity object using Java reflection. By the end, you'll understand the detailed steps involved in the activity lifecycle from creation to launch.