Activity and task management
Two services divide the work of running apps: ActivityTaskManager owns activities and the back stack, and ActivityManager owns processes and everything else. The split explains most of what a launch actually costs.
startActivity() looks like a local call. It is not. It is a request to a process you do not
control, which has to decide whether you are allowed to make it, which app should handle it, whether
that app is already running, and where the resulting screen goes in the back stack.
TL;DR#
- owns activities, tasks and the back stack. owns processes, services, broadcasts, providers and OOM importance.
- The two were one class until Android 10 (API 29Android 10), when
ActivityTaskManagerServicewas split out ofActivityManagerService.1 - Apps still call most public APIs through
ActivityManager, but the internal Binder name for the activity side isactivity_task, notactivity. - A launch is asynchronous from the caller's point of view:
startActivityreturns when the system accepts the request, not when the target is visible. - The app side of the conversation is
ActivityThreadand itsApplicationThreadBinder object. The system calls into the app to make it create and resume an activity.
Mental model#
Imagine a theatre.
The stage manager decides what is on stage, in what order, and what comes back when a scene ends. That is ActivityTaskManager: the running order, the set changes, the queue of scenes behind the current one.
The company manager hires the actors, decides who is in the building, sends people home when the budget runs out, and keeps a list of who is essential to tonight's show. That is ActivityManager: processes, importance, and the decision to kill.
They work in the same office because they need the same information. The stage manager cannot decide whether the next scene is possible without knowing whether the actors are already in the building.
Where the analogy breaks#
A theatre has one stage. An Android device has several — split screen, picture-in-picture, multiple
displays — and a task can be visible but paused, or running but not visible, or cached and
resumable. The state machine is much larger than "on stage" and "off stage", which is why
dumpsys activity activities prints so much.
The big picture#
Text description of this diagram
A sequence diagram for an activity launch. The calling app calls startActivity, which reaches Instrumentation in the same process, which makes a Binder call to ActivityTaskManager in system_server. ActivityTaskManager checks the caller and resolves the intent, possibly by asking PackageManager. If no suitable process exists, ActivityManager asks Zygote to fork a new process, which specialises into the target app and runs ActivityThread.main. The system then sends bindApplication and launchActivity transactions to the new process. The target runs onCreate and setContentView. Meanwhile the caller's startActivity has already returned — it returned when the request was accepted, not when the activity appeared.
Read the diagram from both ends. The caller's thread does almost nothing: one Binder transaction and it is done. The target's thread does all the expensive work: class loading, application creation, layout inflation, measurement, drawing. That asymmetry is the design. A launcher must never wait for another app's layout pass.
Deep dive#
The split, and why it happened#
Before Android 10, ActivityManagerService was one very large class that managed both processes and
activities. Those are different problems with different state:
| ActivityTaskManager | ActivityManager | |
|---|---|---|
| Owns | Activities, tasks, the back stack, launch modes | Processes, services, broadcasts, providers |
| Binder name | activity_task | activity |
| Key state | Task, ActivityRecord, TaskFragment | ProcessRecord, ServiceRecord, ConnectionRecord |
| Importance | Which activity is visible and focused | Which process is worth killing |
| Public reach | ActivityTaskManager (added API 29) | ActivityManager (API 1) |
The split made each service smaller, gave each its own lock (the activity manager's global lock was
a notorious contention point), and let the two evolve at different speeds. It also created a
persistent confusion: apps that want activity information often still go through ActivityManager,
which forwards to the task manager internally.
What a launch actually does#
The interesting part of startActivity is the set of decisions made between the caller and the
target. They happen in a class called ActivityStarter, and they are roughly:
- Is the caller allowed? Background activity starts have been restricted since API 29Android 10.2 A caller without a visible window, a recent notification or a foreground service may simply be refused.
- Resolve the intent. For an explicit intent, the component is already known. For an implicit one, PackageManager matches intent filters and the caller's package visibility.
- Find or create the task. Launch modes and flags decide whether this goes into the current task, a new task, or an existing task with a matching affinity.
- Find or create the process. If the target app is already running, reuse it. If not, ActivityManager asks Zygote to fork.
- Create the activity record and the window. The window is created before the activity, because the window is what the system needs in order to lay out and animate.
- Tell the app to launch. A
ClientTransactioncarries the sequence of lifecycle items to execute.3
Steps 1 to 5 happen in system_server. Only step 6 crosses into the app, and it crosses as an
asynchronous transaction — the system does not block waiting for onCreate to finish.
The app side: ActivityThread and ApplicationThread#
Every app process has exactly one ActivityThread. Despite the name it is not a thread you create;
it is the object that runs your app's main thread and owns the message loop.
The system does not call your Activity directly. It calls a Binder object called
ApplicationThread, which is an inner class of ActivityThread, and that object posts work onto
the main thread's Handler. This indirection is the whole point: the Binder thread that receives
the transaction is not the main thread, and lifecycle callbacks must run on the main thread.
The modern transport is ClientTransaction. A transaction is a list of ClientTransactionItems —
LaunchActivityItem, ResumeActivityItem, PauseActivityItem, StopActivityItem,
DestroyActivityItem — executed in order. It replaced a series of one-off Binder methods
(scheduleLaunchActivity, scheduleResumeActivity, …) with a single transaction type, which made
it possible to batch and to reason about ordering.
Tasks, launch modes and the back stack#
A is a stack of activities that the user experiences as one thing. The back
stack is the ordered set of tasks. The rules are set by launchMode in the manifest and by intent
flags at runtime, and the combinations are the source of most navigation bugs in real apps.
The four classic launch modes are standard, singleTop, singleTask and singleInstance. They
control whether a new instance is created, whether an existing instance is reused, and whether the
activity lives in its own task. singleTask in particular is frequently misunderstood: it means
"at most one instance in the system, in a task of its own", not "don't create duplicates in this
task".
Process importance: the other half#
Everything above is about activities. The other service is about the processes those activities live in, and its central output is a number: the .
ActivityManagerService computes an oom_adj (and, on modern kernels, an oom_score_adj) for
every process, based on what it is doing. A process with a visible activity is important; a process
with only a cached activity is not. The kernel's low-memory killer and the userspace lmkd daemon
use that number to decide who dies first.
The computation is continuous, not on-demand. Every time an activity starts, stops or is destroyed,
the importance of every process that might be affected is recalculated. That is why
dumpsys activity processes is so long: it is a snapshot of a system that is always recomputing.
The pieces you can actually watch#
dumpsys activity is the window into all of this, and it has subcommands that map onto the split:
dumpsys activity activities— tasks, activity records, their states, and which is resumed.dumpsys activity processes— every process, itsoom_adj, and why it has that value.dumpsys activity services— running services and their bindings.dumpsys activity broadcasts— registered receivers and pending broadcasts.
The volume is large because the state is large. The useful habit is to read one section at a time and to look for the reason field, which is the system explaining its own decision.
How it evolved#
| API | Version | What changed |
|---|---|---|
| 1 | 1.0 |
|
| 4 | 1.6 Donut |
|
| 5 | 2.0 Eclair |
|
| 8 | 2.2 Froyo |
|
| 9 | 2.3 Gingerbread |
|
| 11 | 3.0 Honeycomb |
|
| 13 | 3.2 Honeycomb |
|
| 14 | 4.0 Ice Cream Sandwich |
|
| 16 | 4.1 Jelly Bean |
|
| 17 | 4.2 Jelly Bean |
|
| 18 | 4.3 Jelly Bean |
|
| 19 | 4.4 KitKat |
|
| 20 | 4.4W KitKat (Wear) |
|
| 21 | 5.0 Lollipop |
|
| 22 | 5.1 Lollipop |
|
| 23 | 6.0 Marshmallow |
|
| 24 | 7.0 Nougat |
|
| 26 | 8.0 Oreo |
|
| 27 | 8.1 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 31 | 12 |
|
| 32 | 12L |
|
| 33 | 13 Tiramisu |
|
| 34 | 14 Upside Down Cake |
|
| 35 | 15 Vanilla Ice Cream |
|
| 36 | 16 Baklava |
|
| 37 | 17 Cinnamon Bun |
|
The trajectory is consistent: fewer implicit behaviours, more explicit state, more of the decision
recorded rather than reconstructed. Background activity starts were restricted at
API 29Android 10. PendingIntent mutability became mandatory at API 31Android 12. Android 17
(API 37Android 17) stops recreating activities for keyboard, navigation and colour-mode
configuration changes by default, which removes a whole class of "why did my activity restart"
questions.
Under the hood#
- ActivityTaskManagerService.javaactivities, tasks, the back stack; the service behind the activity_task binder name
- ActivityStarter.javathe decision chain for a launch: permission, resolution, task, process
- ActivityManagerService.javaprocesses, services, broadcasts, providers, and the OOM adjustment computation
- ActivityThread.javathe app side: main thread, ApplicationThread, and transaction execution
- ClientTransaction.javathe transaction type that carries a lifecycle sequence into an app
Curiosity corner#
ActivityThreadis not a Thread. It extendsObjectand runs on the main thread; the name is a historical artefact from a design where it may have been one.- The launcher is just an app. It starts activities through exactly the same path your app uses, with no special API. The only difference is which permissions it holds.
- A "cold start" is mostly not your code. Process fork, application creation and resource
loading usually dominate
onCreate; the drawing is a smaller share than most profiling sessions suggest.
Try it#
adb shell dumpsys activity activities | head -60
adb shell dumpsys activity processes | grep -A4 "com.example.app"
Example output
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
* Task{7f3a1c2 #1234 type=standard A=10123:com.example.app}
* ActivityRecord{8a2b3c4 u0 com.example.app/.MainActivity t1234}
state=RESUMED visible=trueThe A= field is the app's UID; t1234 is the task id. In the process dump, look for oom= and
adj= — those are the numbers the low-memory killer reads.
Check yourself#
Check yourself
How does system_server's process come into existence at boot?
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsAndroid platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
- docsPerubahan perilaku: aplikasi yang menargetkan API level 28+developer.android.comaccessed 2026-10-27↩
- docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩
- docsالمهام وحزمة التطبيقات الخلفية | App architecturedeveloper.android.comaccessed 2026-10-27↩
- docsProcesses and threads overview | App qualitydeveloper.android.comaccessed 2026-10-27↩
- docsZygotesource.android.comaccessed 2026-10-27↩
- docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩