AOSP Framework & Internals
5 min read

Activity

A deep dive into the Activity component, exploring its lifecycle, launch modes, and representation inside the System Server.

Mobile operating systems face a fundamental trust issue. If a user presses the Home button, the system needs immediate control over what is visible and what runs in the background. An application cannot be trusted to background itself efficiently. The operating system must forcefully orchestrate these state changes to preserve memory and battery life.

The Activity framework is Android's solution to this problem. While it looks like a simple canvas for user interface components, an activity functions as a complex state machine managed remotely. Your application process does not control its own activities. The System Server dictates when an activity is created, paused, or destroyed.

Remote orchestration explains why we have lifecycle callbacks. These methods are not random events triggered by your application logic. Instead, they act as strict commands sent directly from the System Server.

The Lifecycle Pipeline

Process boundaries force the system to establish a communication channel to broadcast state changes. When a user navigates away from your app, the operating system must inform your application to stop rendering and save its state. The ActivityManagerService (AMS) handles this coordination from within the System Server.

The sequence diagram below shows the exact communication flow when a user presses the Home button. Visualizing this boundary helps you understand how lifecycle events are pushed to your app rather than pulled. Pay attention to the Binder IPC boundary separating the two processes.

When the hardware detects a Home button press, the AMS determines which activity is currently in the foreground. It then sends a Binder IPC message to the ActivityThread running in your application process. Your ActivityThread receives this message and invokes the onPause callback on the main thread. After your app finishes pausing, it notifies the AMS, which then commands your app to stop.

If you block the main thread during onPause with heavy work, you freeze the entire AMS transition pipeline. The AMS will wait for your app to finish pausing before it can render the home screen or launch the next app. Such delays create a sluggish user experience and easily trigger an Application Not Responding crash.

Bending the Back Stack

Mobile applications often need to launch an existing screen instead of creating a duplicate. Users expect the back button to behave predictably, returning them to their previous context. Android manages this expectation using a Last-In-First-Out task stack by default. Complex application flows frequently require altering this standard behavior.

Developers modify how the system places an activity into a task stack using launch modes. You define these in your manifest or apply them via Intent flags at runtime. These modes instruct the AMS on how to route a new Intent to an existing or new activity instance.

  • standard: The system creates a new instance of the activity every time.
  • singleTop: If the activity is already at the absolute top of the stack, the AMS routes the Intent to onNewIntent instead of creating a duplicate instance.
  • singleTask: The activity becomes the root of a new task. If it already exists elsewhere, the AMS destroys all activities above it and routes the Intent to onNewIntent.
  • singleInstance: Similar to singleTask, but the activity is the only member of its task.

Choosing the wrong launch mode breaks the user's expected back-stack navigation. Use these modes only when the default standard behavior fundamentally conflicts with your application flow. The system needs a way to track all of these modes and states internally.

The System Server's Source of Truth

The AMS must track the state of hundreds of activities across dozens of background applications. It cannot keep the actual Java Activity objects in memory because they belong to the individual application processes. Platform architecture requires a lightweight representation of every screen.

To solve this, the AMS uses an internal data structure called ActivityRecord. Each record acts as the system's single source of truth for an activity.

The flowchart below illustrates the relationship between the system's ActivityRecord and the application's Activity. It highlights how the AMS tracks metadata without holding UI resources. Notice the mapping between the lightweight record and the heavy application instance.

Inside the System Server, the ActivityRecord stores crucial metadata like the specific launch mode, the current lifecycle state, and the memory footprint. The record also holds the Binder token used to communicate with the actual application process. For a platform engineer, the ActivityRecord is the true activity.

Platform engineers often need to verify that launch modes are working or debug broken navigation flows. You can inspect these internal records using the dumpsys tool. This command forces the AMS to dump its current knowledge of all activity states.

adb shell dumpsys activity activities

Tip: Analyzing this output is invaluable for debugging navigation issues. A common mistake is assuming your activity was destroyed just because it left the screen, when the dumpsys output clearly shows it still lingering in a stopped state.

System architecture ensures the operating system remains responsive even when applications misbehave. The AMS strictly manages the lifecycle transitions, but it relies on your application to execute those transitions quickly. When you encounter a frozen screen during testing, you will know exactly which process is holding up the transition. So what happens if your application ignores the AMS and takes too long to pause? The system takes matters into its own hands, which leads directly to how Android handles Application Not Responding errors.