Tasks &
Launch Modes
Android navigation internals

Every Activity lives inside a Task. How it gets there — and whether it shares space with others — is controlled by launch modes and Intent flags. Mastering this is the key to predictable navigation.

standard singleTop singleTask singleInstance Intent flags Task affinity Back stack onNewIntent()
01 · Foundation

What is a Task?

A Task is a collection of activities that users interact with as a unit. It defines a "workflow" — a cohesive experience that starts when the user launches an app and ends when they press Back all the way out. Android maintains tasks separately from your app's process.

Think of a Task as a stack of screens. The user always interacts with the screen at the top. When they press Back, the top screen is popped and the one beneath it becomes active. This is the Android back stack.

Key insight: A Task is not tied to a single app. When you open Gmail from your app via an Intent, Gmail's Activity can be pushed onto your task's back stack. When the user presses Back, they return to your app — not to Gmail's home screen.

Tasks vs processes

These are completely independent concepts that developers often confuse:

Concept
What it manages
Android can kill it?
Process
Memory, CPU, the JVM — where code runs
Yes — when memory is low
Task
Navigation history — which Activities are stacked
No — task state is preserved even after process kill
Activity
A single screen and its UI
Object yes, record in task no

When the system kills your process to reclaim memory, the Task record survives. When the user taps your app icon again, Android recreates the Activities from the task's record, restoring the back stack using the saved instance state bundles.

The Recents screen

The Recents screen (Overview / App Switcher) shows one card per Task, not per app. An app can have multiple tasks — each gets its own card. This is why opening a document link from Gmail might show two Gmail cards in Recents: one for the main inbox task and one for the document viewer task.

02 · Core Concept

The Back Stack

The back stack is a LIFO (Last In, First Out) stack of Activity instances within a Task. Every time you call startActivity(), a new Activity is pushed onto the stack. Every time the user presses Back (or calls finish()), the top Activity is popped and destroyed.

Back Stack — Live Demo
Task: com.myapp
Press "startActivity(B)" to push an Activity onto the back stack.

What happens on Back press

When the user presses Back on the last Activity in a Task (the root Activity), the Task is moved to the background. The process may keep running, but the task is no longer visible. If the user returns via Recents or the launcher icon, the task resumes exactly where they left it — as long as the system hasn't killed the process.

Important: Do not override onBackPressed() to call finish() manually — that's what the system already does. Only override it when you need custom behavior like "confirm before exit" dialogs or custom transition handling.

Multiple tasks in the background

Android keeps multiple tasks in memory simultaneously. When you switch apps (via Recents or the launcher), Android brings the selected app's task to the foreground. The previously visible task moves to the background — its Activities go through onPause → onStop but are not destroyed. They're simply hidden, waiting.

03 · Task Affinity

Task Affinity

Every Activity has a task affinity — a string identifier for which task it "prefers" to belong to. By default, all Activities in an app share the same affinity: the app's package name. This means they naturally live in the same task.

Task affinity only matters in two specific situations: when an Activity is launched with FLAG_ACTIVITY_NEW_TASK, or when an Activity has allowTaskReparenting="true".

AndroidManifest.xml
<!-- Default: affinity = package name -->
<activity android:name=".MainActivity" />

<!-- Custom affinity — prefers a different task -->
<activity
    android:name=".DocumentActivity"
    android:taskAffinity="com.myapp.documents"
    android:launchMode="singleTask" />

<!-- No affinity — never shares a task -->
<activity
    android:name=".IsolatedActivity"
    android:taskAffinity="" />
Task A · com.myapp
MainActivityaffinity: com.myapp
SettingsActivityaffinity: com.myapp
Task B · com.myapp.documents
DocumentActivityaffinity: com.myapp.documents
Same app · Two tasks in Recents · Different affinities

allowTaskReparenting

When set to true, an Activity can move between tasks. If Activity C (with affinity "com.other") was started by your app and placed in your task, the next time "com.other" is brought to the foreground, Activity C will reparent — it migrates from your task to the "com.other" task automatically. This is how Android handles email links to other apps.

04 · Launch Mode

standard

The default. Every call to startActivity() creates a brand new instance of the Activity and pushes it onto the current task's back stack — regardless of whether an instance already exists anywhere in the stack.

A
Always new
A new instance every call

Calling startActivity(A) when A is already at the top creates a second instance of A on the stack. The user must press Back twice to dismiss both.

This can lead to the infamous "double-A" stack if a notification or deep link always starts from the Activity rather than checking existing state.

Stack after: A → B → A → A
A
B
A³ ★
When to use
Correct scenarios

The vast majority of Activities. Any screen that can logically exist multiple times in a navigation flow — detail screens, edit forms, sub-pages.

If you're using Jetpack Navigation Component, you rarely deal with launch modes directly — NavController handles this correctly by default.

manifest declaration
android:launchMode="standard"  (or omit — it's default)
05 · Launch Mode

singleTop

If an instance of the Activity is already at the top of the back stack, Android reuses it instead of creating a new one. It calls onNewIntent(intent) on the existing instance. If the Activity is not at the top (even if it exists elsewhere in the stack), a new instance is created normally.

onNewIntent(intent): When the existing top instance is reused, the new Intent is delivered here. Override it and call setIntent(intent) to update getIntent(), then process the new data. The Activity does NOT go through onCreate again.
Top = reuse
Not at top = new instance

Scenario 1: Stack is A→B (B at top). Start B again → onNewIntent(B) called on existing B. Stack stays A→B.

Scenario 2: Stack is A→B→C. Start B → new B² created. Stack becomes A→B→C→B².

Scenario 1: B already on top
A
B ↻
 ← onNewIntent()
When to use
Perfect fits

Push notifications that deep-link to an Activity — prevents stacking the same screen repeatedly when the user taps multiple notifications.

Search results where search fires a new intent on the same screen. Each search updates the displayed results without creating nested screens.

manifest
android:launchMode="singleTop"
Kotlin · handling onNewIntent
// AndroidManifest.xml
// android:launchMode="singleTop"

class NotificationActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleIntent(intent)   // handle initial launch
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)       // update getIntent() for future calls
        handleIntent(intent)   // same handler — no duplicated logic
    }

    private fun handleIntent(intent: Intent) {
        val notifId = intent.getStringExtra("notif_id") ?: return
        viewModel.loadNotification(notifId)
    }
}
06 · Launch Mode

singleTask

The most powerful and most misunderstood launch mode. The system ensures only one instance exists across the entire task. When the Activity is launched:

If an instance already exists anywhere in its task → the system brings that task to the foreground, clears everything above it in the stack, and calls onNewIntent(). If no instance exists → creates a new one (potentially in a new task if the affinity differs).

Stack destruction warning: When onNewIntent fires for a singleTask Activity, every Activity above it in the back stack is destroyed. The user loses their navigation history back to that point. This is intentional for home screens but catastrophic for detail screens — use with care.
!
Clears above
Destructive — back stack trimmed

Stack: Home→List→Detail→Edit. Launch Home (singleTask) → Edit and Detail are destroyed. Stack becomes: Home. onNewIntent fires on Home.

This makes singleTask perfect for the root/home Activity — it's exactly the behavior users expect when tapping the app's home icon.

Before → After launching Home (singleTask)
Home
List
Detail
Edit
↓ launch Home (singleTask) →
Home ↻
List, Detail, Edit destroyed
When to use
Correct use cases

Home/root Activity — the one the app launcher icon opens. Tapping home should always bring you to the start of your app's flow.

Login Activity — once logged in, navigating to login from deep inside should clear the authenticated stack.

Browser-like pivots — where re-entering a section should reset its internal stack.

manifest
android:launchMode="singleTask"
07 · Launch Mode

singleInstance

The most isolated mode. The Activity gets its own private task — no other Activity can ever be added to that task. It is the only Activity in its task, always. Any Activity launched from a singleInstance Activity goes into a different task.

When re-launched: the existing instance is reused and onNewIntent() is called, exactly like singleTask. But unlike singleTask, there's nothing above it to clear — it's always alone.

Own private task
Completely isolated

If your app's Activity A launches singleInstance Activity S, and S then launches B — B goes into a new task (your app's default task), not into S's private task.

Pressing Back from S takes the user to whatever task was in the foreground before, not necessarily to A.

Task isolation
Task 1: com.myapp
A
B (from S)
Task 2: S's private task
S (alone)
When to use
Very specialized

System-level screens that must be globally unique across the device — like the Android Dialer during a call, a system VPN screen, or a phone launcher.

Shared functionality between apps — if your app exposes an Activity that other apps can reuse and should always be a single instance.

In practice, very few production apps need singleInstance.

manifest
android:launchMode="singleInstance"
Mode
If instance exists at top
If instance exists NOT at top
standard
New instance created
New instance created
singleTop
Reuse → onNewIntent()
New instance created
singleTask
Reuse → onNewIntent() + clear above
Reuse → onNewIntent() + clear above
singleInstance
Reuse → onNewIntent() (alone in task)
Reuse → onNewIntent() (alone in task)
08 · Intent Flags

Intent Flags

Intent flags give you per-launch control over how an Activity is placed on the back stack — without changing the manifest. They override or augment the declared launch mode for a single call. They're especially important for notification deep links, shortcuts, and cross-app launches.

FLAG_ACTIVITY_NEW_TASK
Common

Start the Activity in a new task. If a task with the Activity's affinity already exists, that task is brought to the foreground instead of creating a new one. Required when starting an Activity from a non-Activity context (Service, BroadcastReceiver, Application). Does NOT guarantee a new task if one with matching affinity exists.

Kotlin
// From a Service or BroadcastReceiver
val intent = Intent(this, MainActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
FLAG_ACTIVITY_CLEAR_TOP
Navigation

If the target Activity already exists in the task, destroy all Activities above it and bring it to the top. Combined with FLAG_ACTIVITY_SINGLE_TOP, it calls onNewIntent() on the existing instance instead of destroying and recreating it. This combination is the Intent-flag equivalent of singleTask.

Without SINGLE_TOP: the existing instance is destroyed and recreated (onCreate called). With SINGLE_TOP: the existing instance is reused and onNewIntent called.

Kotlin
// Navigate "home" clearing all screens above
val intent = Intent(this, HomeActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or
            Intent.FLAG_ACTIVITY_SINGLE_TOP
}
startActivity(intent)
FLAG_ACTIVITY_SINGLE_TOP
Reuse

The Intent-flag equivalent of launchMode="singleTop". If the target Activity is already at the top of the stack, reuse it (call onNewIntent) instead of creating a new instance. Only applies to the current call — does not change the manifest declaration permanently.

FLAG_ACTIVITY_NO_HISTORY
Special

The Activity will not be kept in the history stack. As soon as the user moves away from it, it is immediately finished. Useful for login/auth screens you never want the user to return to via Back, or splash screens.

FLAG_ACTIVITY_CLEAR_TASK
Destructive

Clears the entire task before launching the Activity. Must be used with FLAG_ACTIVITY_NEW_TASK. All Activities in the target task are destroyed. Use for logout flows — after logout you want the user to start fresh in the login screen with nothing to go Back to.

Kotlin · Logout pattern
fun logout() {
    authManager.signOut()
    val intent = Intent(this, LoginActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    startActivity(intent)  // all activities destroyed, LoginActivity fresh
}
FLAG_ACTIVITY_REORDER_TO_FRONT
Reorder

If the Activity already exists anywhere in the task, move it to the front without destroying anything. Unlike CLEAR_TOP, the Activities that were above it are not destroyed — they just move behind it. The back stack order is reshuffled. Useful for tab-like navigation where you want to preserve all screens.

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
UX

The Task will not appear in the Recents (Overview) screen. Useful for authentication flows, OTP entry screens, or any transient Activity that the user should not be able to return to from the app switcher. Can also be set in the manifest with android:excludeFromRecents="true".

09 · Interactive

Launch Mode Playground

Select a launch mode, then tap activities to push them onto the stack. See exactly how each mode affects the back stack in real time.

Interactive Demo
Task 1
Select a launch mode and launch activities to see what happens.
10 · Real World

Real-world Scenarios

Knowing the theory is one thing. Here's how launch modes and flags map to actual product requirements:

Push notification opens the same screen repeatedly
Problem

Symptom: User taps 3 notifications. Presses Back 3 times before reaching the previous screen.

Fix: Use launchMode="singleTop" on the notification target Activity, and set FLAG_ACTIVITY_SINGLE_TOP in the PendingIntent. Each notification updates the existing instance via onNewIntent instead of creating a new one.

Better fix: Use TaskStackBuilder to construct a synthetic back stack, combined with singleTop on the target.

After logout, user can press Back to reach authenticated screens
Security

Symptom: User logs out, lands on LoginActivity, presses Back and sees the previous user's data.

Fix: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK when launching LoginActivity on logout. This destroys the entire task history — the Back button on Login exits the app.

Kotlin
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
Deep link from another app opens inside wrong task
Deep Links

Symptom: User taps a link in Chrome, gets to ProductDetailActivity. Presses Back — goes back to Chrome instead of your app's home screen.

Fix: Use TaskStackBuilder with addParentStack() in the PendingIntent. This builds a synthetic back stack (Home → Category → Product) so Back navigates within your app.

Kotlin · TaskStackBuilder
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addParentStack(ProductDetailActivity::class.java)
stackBuilder.addNextIntent(Intent(this, ProductDetailActivity::class.java).apply {
    putExtra("product_id", productId)
})
val pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
Bottom navigation creates duplicate screens on re-tap
Navigation

Symptom: User on Home tab, navigates to Settings, taps Home tab again — gets a duplicate Home on the stack instead of returning to the original.

Fix: Use FLAG_ACTIVITY_REORDER_TO_FRONT for tab switches to bring existing instances forward, or better — use Jetpack Navigation with a NavGraph that handles this automatically via popUpTo and launchSingleTop=true in the navigation XML.

App launched from notification while already open lands wrong
Notifications

Symptom: App is open on screen A. User receives a notification, taps it, expects to land on screen B (notification target). Instead they land on a separate task with just screen B and no app history.

Fix: Check whether your app's task is already running before constructing the PendingIntent. Use FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP if you want to navigate within the existing task, or build a proper stack with TaskStackBuilder.