Launch Modes in Android - with Examples
Tags: android, tutorial, learning, androiddevelopment
Published: 2023-11-07 23:29:03

---
title: Launch Modes in Android - with Examples
published: true
date: 2023-11-07 22:30:52 UTC
tags: android, tutorial, learning, androiddevelopment
canonical_url: https://blog.nicholasfragiskatos.dev/launch-modes-in-android-with-examples
---

A _Task_ is a model that the Android system uses to manage a collection of _Activities_ that the user is interacting with through some application's workflow. In the simplest, default case we would have a one-to-one mapping between one task and one application's set of Activities. When a user is navigating through a multi-activity application, each new Activity that is launched is added to the associated Task.

To keep everything organized, a Task places its Activities in a _Back Stack_; with each successively opened Activity being added to the top of the stack. This is also what helps facilitate the back navigation when the user presses the back button or swipes back; the current activity gets popped off the stack to be replaced with the Activity underneath it.

As noted earlier, one Task for one application's set of Activities is the default behavior. However, the Android framework does provide the developer with the power to manipulate this behavior either through the manifest file when declaring the Activity to the system, or by using Intent flags.

_In this article, I want to focus on the Android manifest file_ and all the different ways in which we can manipulate the Activity-Task association behavior when defining different Activities in the app's manifest.

I've created a toy app to easily exemplify all the different behaviors. I will provide small code snippets where needed, but the full sample project can be found [here on GitHub](https://github.com/nfragiskatos/AndroidLaunchModes).

## Project Setup

I created a brand new project using Android Studio's "_Empty Views Activity_" template. Then I created six new Activities:

1. `ActivityA`

2. `ActivityB`

3. `ActivityC`

4. `ActivityD`

5. `ActivityE`

6. `ActivityF`

Each Activity is basically the same in terms of layout and functionality. Their layout contains a `TextView` at the top to easily distinguish between each Activity. Then they also contain six buttons, each to open one of the Activities when clicked.

![Activities A through C](https://cdn.hashnode.com/res/hashnode/image/upload/v1699277958959/1259bac6-9a42-443f-8fe5-059bef0d2992.png)

![Activities D through F](https://cdn.hashnode.com/res/hashnode/image/upload/v1699277975589/860d21a1-0887-4487-b836-67419ab8e364.png)

Also, in each Activity, I overrode the `onNewIntent(...)` function and displayed a `Toast` each time it was called. This function will be important later. An example of what it looks like in `ActivityA`:

```kotlin
class ActivityA : AppCompatActivity() {
    // ...

    override fun onNewIntent(intent: Intent?) {  
        super.onNewIntent(intent)  
        Toast.makeText(  
            this,  
            "onNewIntent called for Activity A",  
            Toast.LENGTH_LONG,  
        ).show()  
    }
}

```

Lastly, instead of just renaming `MainActivity`, I updated the _AndroidManifest.xml_ to make `ActivityA` the default launched Activity instead of `MainActivity`.

```xml
<manifest ...>  
  ...
<application ...>  
 ...
    <activity  
        android:name=".launchactivities.ActivityA"  
        android:exported="true"  
        <intent-filter>  
            <action android:name="android.intent.action.MAIN" />  

            <category android:name="android.intent.category.LAUNCHER" />  
        </intent-filter>  
    </activity>  
    <activity  
        android:name=".MainActivity"  
        android:exported="true">  
    <!-- <intent-filter>-->  
    <!-- <action android:name="android.intent.action.MAIN" />-->  

    <!-- <category android:name="android.intent.category.LAUNCHER" />-->  
    <!-- </intent-filter>-->  
    </activity>  
</application>  

</manifest>

```

## A Quick Note on a Task's _Affinity_

Each Activity, whether explicitly defined or not, has an _Affinity_. For a Task, its Affinity would be the Affinity for the Activity at the root of the Task. This holds even if there are multiple Activities with differing Affinities all in one Task (this is possible if all activities are using the `standard` Launch Mode that we'll discuss later). For example, say a Task has a Back Stack of the following Activities with Affinities:

A (`.MyAffinity`) -> B (`.MyOtherAffinity`) -> C (`.YetAnotherAffinity`)

Then the Affinity for the task would be `.MyAffinity`.

An Affinity denotes to Android which Task an Activity prefers to be in. By default, every Activity in an application has the same Affinity. However, there are no hard rules on which Affinity an Activity can have. All Activities in the same application can each have distinct Affinities, and even one Activity from one application can share an Affinity with an Activity from another application.

An Affinity is only one criterion for the OS to determine which Task to place a new Activity in. As we will see later, the Affinity combines with the _Launch Mode_ to ultimately determine the placement of the newly opened Activity.

To set the Affinity of an Activity, you can use the `taskAffinity` XML attribute for the `<activity>` tag in the _AndroidManifest.xml_. If the `taskAffinity` attribute is not set, then the default Affinity is the value for the `namespace` property in the _build.gradle_ file.

```xml
<manifest ...>  
  ...
<application ...>  
 ...
    <activity  
        android:name=".launchactivities.ActivityB"  
        android:taskAffinity="my.Affinity"
        android:exported="true"   
    </activity>    
</application>  
</manifest>


// build.gradle
android {   
    ...
    // default Affinity value
    namespace = "com.nicholasfragiskatos.androidlaunchmodes"      
}

```

### Valid `taskAffinity` Values

The only rule I could find about appropriate values is from this part of the [documentation](https://developer.android.com/guide/components/activities/tasks-and-back-stack#Affinities) where it says,

> The `taskAffinity` attribute takes a string value that must be different than the default package name declared in the `<manifest>` element.

However, I noticed that the value requires a period separator somewhere in the name. Having something like `taskAffinity="MyAffinity"` causes a build error:

> Error running 'app': The application could not be installed: INSTALL\_PARSE\_FAILED\_MANIFEST\_MALFORMED

The following values seem to be OK:

- .myAffinity

- my.Affinity

- myAffinity.

- ..myAffinity

- . (yes, just a period)

## Viewing Tasks on the System

I could not find a good graphical way to view all running Tasks on an emulator in Android Studio. There were some options, but they appeared to be old and incompatible with modern versions of the IDE. The best alternative I could find is using the Android Debugging Bridge (ADB) on the command line and looking through the output of the following command:

```bash
adb shell dumpsys activity recents

```

This will give you a list of recent Tasks, and show the list of Activities for the Task, as well as the Affinity, among other information. For example: (output formatted and abbreviated)

```bash
Recent tasks: Recent #0:{54288a5 #148 type=standard A=10164:.com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{50c032f u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t107}, 
ActivityRecord{eca6d90 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t107}, 
ActivityRecord{a3a3a7 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t107}, 
ActivityRecord{419e7d8 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t107}
]

```

### Recent Numbering

The numbering changes based on which Task you viewed most recently. It's best to ignore the number and follow the Task ID (#148 from the example below):

> Recent #0:{54288a5 # **148** type=standard A=10164:.com.nicholasfragiskatos.androidlaunchmodes}

## Launch Modes

Launch Modes are a way for the user to specify to the system how a new instance of an Activity should be related to the current Task. We can specify a Launch Mode in either the _AndroidManifest.xml_ when defining the Activity to the system or in Intent flags when launching the new Activity. There are five different Launch Modes.

### `standard`

For this Launch Mode, a new instance of the Activity is created each time regardless of if there already exists an instance of the Activity in the Task. This is the default behavior. You can have as many of the same instances of an Activity across multiple tasks. For instance, all three of these examples are valid states when using the `standard` Launch Mode:

![Examples of valid standard launch mode states](https://cdn.hashnode.com/res/hashnode/image/upload/v1699280350203/2e4cb82e-ae85-4e52-a293-612ab826dd33.png)

In the example project, to demonstrate this, launch `ActivityA` multiple times.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: 

... 

Activities=[ 
ActivityRecord{449ced3 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t114}, 
ActivityRecord{5d70c45 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t114}, 
ActivityRecord{7a9c8d3 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t114}, 
ActivityRecord{948d5c1 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t114}, 
ActivityRecord{bffb7c9 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t114}
]

```

> 💡
> By default, the new instance of the Activity will be created and placed in the same Task that it was launched from. However, as we will see later, depending on other factors it is not guaranteed.

### `singleTop`

This Launch Mode is almost identical to `standard`. The only difference is that _if the Activity you are trying to launch already has an existing instance at the top of the current Task's stack_, then instead of creating a new instance, the system will just call the Activity's `onNewIntent(...)` function.

Considering the example below with the state of the stack being A -> B -> C -> D -> E,<sup>1</sup> if we try and launch E again no new instance of `ActivityE` is created since it's at the top. Instead, the system re-uses the existing instance and just calls `onNewIntent(...)`.<sup>2</sup> However, when we try and launch `ActivityC` again, an instance already exists on the stack, but it's not at the top, so a new instance is created and added to the stack.<sup>3</sup>

![example of singleTop launch mode behavior](https://cdn.hashnode.com/res/hashnode/image/upload/v1699390482677/84eb8c53-ed3a-4827-b057-8eb679f45399.png)

In the example project, to demonstrate this, first, we set the Launch Mode in the manifest for `ActivityE`:

```xml
<manifest>  
    ... 
<application  
    ...
    <activity  
        android:name=".launchactivities.ActivityE"  
        android:launchMode="singleTop"  
        android:exported="false" />  
    ...
    <activity  
        android:name=".launchactivities.ActivityC"  
        android:launchMode="singleTop"  
        android:exported="false" />
</application>  
</manifest>

```

Then, remember that we overrode the `onNewIntent(...)` function for each Activity to display a Toast:

```kotlin
override fun onNewIntent(intent: Intent?) {  
    super.onNewIntent(intent)  
    Toast.makeText(  
        this,  
        "onNewIntent called for Activity E",  
        Toast.LENGTH_LONG,  
    ).show()  
}

```

Starting Activities A -> B -> C -> D -> E -> E will result in the `onNewIntent(...)` function being called, but no additional instance of `ActivityE` is being added to the stack.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: 

... 

Activities=[ 
ActivityRecord{f04dc64 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t118}, 
ActivityRecord{97af3cc u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t118}, 
ActivityRecord{15ff5c9 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t118}, 
ActivityRecord{6efecbf u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t118}, 
ActivityRecord{5ff0a89 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityE} t118}, 
]

```

However, starting `ActivityC` again will result in it being added to the stack.

```bash
Recent tasks:

Recent #0: 

... 

Activities=[ 
ActivityRecord{f04dc64 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t118}, 
ActivityRecord{97af3cc u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t118}, 
ActivityRecord{15ff5c9 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t118}, 
ActivityRecord{6efecbf u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t118}, 
ActivityRecord{5ff0a89 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityE} t118}, 
ActivityRecord{582171d u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t118}
]

```

### `singleTask`

With this Launch Mode, the goal for the system is to maintain a single instance of the Activity at a time. The system will either re-use an existing instance of the Activity, create a new task with the Activity at the root of the stack or use an existing Task that shares the same Affinity. The behavior depends on whether there is already an existing instance of the Activity in _some_ Task, and then whether there's a Task with the same Affinity.

#### Existing Activity

If the system can find an existing instance of the Activity in either the current Task or another Task then the launched Intent will be routed to the existing Activity to re-use it, and the Activity's `onNewIntent(...)` function is invoked, similar to what happens in `singleTop`. However, what also happens is that all Activities that are above the re-used Activity will be popped off the stack, leaving the re-used activity back at the top of the stack for the Task.

In the following example, for the original state, we have Task #1 and Task #2. `ActivityD` is set as `singleTask` and has `.MyAffinity`. All other Activities have the `standard` Launch Mode with the default Affinity.

If we try and start `ActivityD` again either in Task #1 or Task #2, then the system will find the existing `ActivityD` instance, and route the Intent there. This will result in all other Activities above it being removed from the stack.

![example of singleTask launch mode behavior for existing instance of activity](https://cdn.hashnode.com/res/hashnode/image/upload/v1699390618494/4515769a-2a9b-4529-ab19-c43888c48fde.png)

To demonstrate this in the example project, we update the Launch Mode and Affinity in the manifest for `ActivityD`. Set all other Activities back to `standard` Launch Mode.

```xml
<activity  
    android:name=".launchactivities.ActivityD"  
    android:taskAffinity=".myAffinity"  
    android:launchMode="singleTask"  
    android:exported="false" />

```

Starting Activities A -> B -> C -> D -> E -> A -> B will get us to the original state from the example above where Task #1 has A -> B -> C in its Back Stack, and Task #2 has D -> E -> A -> B in its stack.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks: 

Recent #0: Task{54288a5 #148 type=standard A=10164:.myAffinity}

...

affinity=10164:.myAffinity

...

Activities=[ 
ActivityRecord{e2e12b1 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t148}, 
ActivityRecord{94e9d35 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityE} t148}, 
ActivityRecord{c8cccf7 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t148}, 
ActivityRecord{a2fb2ef u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t148}]

...

Recent #1: Task{cb4f549 #147 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{9d069aa u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t147}, 
ActivityRecord{53edb1a u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t147}, 
ActivityRecord{aa7fbe u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t147}
]

```

If we start `ActivityD` again from either Activity the `onNewIntent(...)` function will be called for `ActivityD` and the Activities above it (E, A, B) will be removed from the stack.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks: Recent #0: Task{54288a5 #148 type=standard A=10164:.myAffinity ...

affinity=10164:.myAffinity

...

Activities=[ 
ActivityRecord{e2e12b1 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t148}
]

```

#### Activity Does Not Exist - Shared Affinity

If the Activity does not already exist _and_ it shares the same Affinity with a Task, then when it is created for the first time it will be placed at the top of the Back Stack of that Task. Every subsequent Activity that is launched will continue to be added to the stack like normal (assuming the other Activities are using the `standard` Launch Mode).

In the diagram below, we start with an original state of A -> B -> C.<sup>1</sup> `ActivityD` has been set as `singleTask` with the default Affinity. Since the Affinity matches, launching `ActivityD` will result in `ActivityD` being placed at the top of the current Task.<sup>2</sup> We can then add A -> B -> C again.<sup>3</sup> If we try and launch `ActivityD` _again_, the OS sees that an instance already exists so it routes the Intent to `onNewIntent(...)` and all Activities above `ActivityD` are popped off the stack until `ActivityD` is at the top.<sup>4</sup>

![example of singleTask launch mode behavior for non existing instance but same affinity](https://cdn.hashnode.com/res/hashnode/image/upload/v1699390735212/bfe6b9c6-5619-440a-8614-f4fbcc64f09b.png)

To demonstrate this in the example project, we update the Launch Mode in the manifest for `ActivityD`. Set all other Activities back to `standard` Launch Mode.

```xml
<activity  
    android:name=".launchactivities.ActivityD"  
    android:launchMode="singleTask"  
    android:exported="false" />

```

Starting Activities A -> B -> C will get us to the original state in the diagram.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks: Recent #0: Task{ed463cf #170 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{e19f818 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t170}, 
ActivityRecord{36def6e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t170}, 
ActivityRecord{96afca3 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t170}
]

```

Starting `ActivityD` places the Activity in the current Task.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks: Recent #0: Task{ed463cf #170 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{e19f818 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t170}, 
ActivityRecord{36def6e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t170}, 
ActivityRecord{96afca3 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t170}, 
ActivityRecord{f802a9e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t170}
]

```

Starting A -> B -> C again from the new Task will create new instances on the stack above `ActivityD.`

`adb shell dumpsys activity recents` output:

```bash
Recent tasks: Recent #0: Task{ed463cf #170 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{e19f818 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t170}, 
ActivityRecord{36def6e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t170}, 
ActivityRecord{96afca3 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t170}, 
ActivityRecord{f802a9e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t170}, 
ActivityRecord{bacf62a u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t170}, 
ActivityRecord{7f315f6 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t170}, 
ActivityRecord{356f985 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t170}
]

```

If we start `ActivityD` again the `onNewIntent(...)` function will be called for `ActivityD` and the Activities above it (A, B, C) will be removed from the stack.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks: Recent #0: Task{ed463cf #170 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{e19f818 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t170}, 
ActivityRecord{36def6e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t170}, 
ActivityRecord{96afca3 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t170}, 
ActivityRecord{f802a9e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t170}]

```

#### Activity Does Not Exist - Different Affinity

If the Activity does not already exist _and_ it _does not_ share the same Affinity with an existing Task, then when it is created for the first time it will be placed at the root of a new Task. Every subsequent Activity that is launched in this new Task will continue to be added to the stack like normal (assuming the other Activities are using the `standard` Launch Mode).

In the diagram below, we start with one Task using the default Affinity (Task #1) with an original state of A -> B -> C.<sup>1</sup> `ActivityD` has been set as `singleTask` with `.MyAffinity`. Since there is no existing instance of `ActivityD`, and no Task shares the same Affinity, starting `ActivityD` will result in a new Task (Task #2) being created and `ActivityD` being placed at the root.<sup>2</sup>. And of course, from here, we can expect normal behavior. Starting A -> B -> C while in Task #2 will create those Activities and place them on the stack.<sup>3</sup> If we try and launch `ActivityD` _again_, the OS sees that an instance already exists so it routes the Intent to `onNewIntent(...)` and all Activities above `ActivityD` are popped off the stack until `ActivityD` is at the top.<sup>4</sup>

![example of singleTask launch mode behavior for non existing instance and different affinity](https://cdn.hashnode.com/res/hashnode/image/upload/v1699390825626/f10f09c9-c258-4939-8fdc-318f6222066b.png)

To demonstrate this in the example project, we update the Launch Mode and Affinity in the manifest for `ActivityD`. Set all other Activities back to `standard` Launch Mode.

```xml
<activity  
    android:name=".launchactivities.ActivityD"  
    android:taskAffinity=".MyAffinity"  
    android:launchMode="singleTask"  
    android:exported="false" />

```

Starting Activities A -> B -> C will get us to the original state in the diagram.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{1aa5d3a #161 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{9fa33e2 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t161}, 
ActivityRecord{1104ae6 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t161}, 
ActivityRecord{a23a934 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t161}
]

```

Starting `ActivityD` creates the new Task.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{28ea774 #162 type=standard A=10164:.MyAffinity}

...

affinity=10164:.MyAffinity

...

Activities=[ 
ActivityRecord{53ca0ba u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t162}
]

```

Starting A -> B -> C again from the new Task will create new instances on the stack above `ActivityD.`

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{28ea774 #162 type=standard A=10164:.MyAffinity

...

affinity=10164:.MyAffinity

...

Activities=[ 
ActivityRecord{53ca0ba u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t162}, 
ActivityRecord{b15cb2e u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t162}, 
ActivityRecord{31480bf u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t162}, 
ActivityRecord{3de0f78 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t162}
]

```

And finally, starting `ActivityD` again will result in `onNewIntent(...)` being called and the stack is cleared until `ActivityD` is at the top.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{28ea774 #162 type=standard A=10164:.MyAffinity}

...

affinity=10164:.MyAffinity

...

Activities=[ 
ActivityRecord{53ca0ba u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t162}
]

```

### `singleInstance`

This Launch Mode is similar to `singleTask` in a few ways:

1. The system maintains a single instance of the Activity at a time

2. Creating an instance of a `singleInstance` Activity will always create a new Task if there is no existing instance already in the system.

3. If an existing instance of the `singleInstance` Activity already exists, then it is re-used and its `onNewIntent(...)` function is invoked.

There's no need to go over the similarities again, but there is one important difference; an Activity with `singleInstance` _will always be the only Activity in a Task_. This means that any other Activity launched by the `singleInstance` Activity will always be placed in a different Task.

In the diagram below, we start with one Task (Task #1) with an original state of A -> B -> C.<sup>1</sup> `ActivityD` has been set as `singleInstance`. Since no existing instance of `ActivityD` exists starting `ActivityD` will result in a new Task (Task #2) being created and `ActivityD` being placed at the root.<sup>2</sup>. Starting E -> F while in Task #2 will create the Activities in Task #1.<sup>3</sup>

![example of singleInstnace launch mode](https://cdn.hashnode.com/res/hashnode/image/upload/v1699390896820/c59d7769-7f4e-4d5c-847b-61348fdcdfcf.png)

To demonstrate this in the example project, we update the Launch Mode and Affinity in the manifest for `ActivityD`. Set all other Activities back to `standard` Launch Mode.

```xml
<activity  
    android:name=".launchactivities.ActivityD"  
    android:taskAffinity=".MyAffinity"  
    android:launchMode="singleInstance"  
    android:exported="false" />

```

Starting Activities A -> B -> C will get us to the original state in the diagram.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{513838 #184 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{1722f24 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t184}, 
ActivityRecord{dc1435 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t184}, 
ActivityRecord{2327270 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t184}
]

```

Starting `ActivityD` creates the new Task.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{5194c82 #185 type=standard A=10164:.MyAffinity}

...

affinity=10164:.MyAffinity

...

Activities=[ 
ActivityRecord{34baff u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD} t185}
]

```

Starting E -> F in the new Task will create new instances in the original Task.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{513838 #184 type=standard A=10164:com.nicholasfragiskatos.androidlaunchmodes

...

affinity=10164:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{1722f24 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA} t184}, 
ActivityRecord{dc1435 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB} t184}, 
ActivityRecord{2327270 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC} t184}, 
ActivityRecord{628a66 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityE} t184}, 
ActivityRecord{dfb1fee u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityF} t184}
]

```

### `singleInstancePerTask`

The last Launch Mode is similar to both `singleTask` and `singleInstance` in a few ways:

1. Like `singleInstance`, the new Activity will always be created in a new Task if there is no existing instance regardless of Affinity.

2. Like `singleTask`, other Activities can exist within the Task.

3. Like `singleTask`, if the `singleInstancePerTask` Activity is already a member of a Task, and if we try and launch the Activity again, then all Activities above it in the stack will be removed.

Both the `singleTask` and the `singleInstance` Launch Mode shared the singleton property, in which the system will always maintain only a single instance of the Activity at a time across all Tasks. Unlike the latter, it is possible for the `singleInstancePerTask` Activity to be started in _multiple_ Tasks while still maintaining only one instance _per_ Task. This behavior is possible through the use of the `FLAG_ACTIVITY_MULTIPLE_TASK`, or `FLAG_ACTIVITY_NEW_DOCUMENT` Intent flags. This article only covers Launch Modes as defined through the manifest, but I thought it important enough to mention.

#### Sharing Affinity

For the `singleInstancePerTask` Launch Mode, the Activity will always be created in a new Task if there is no existing instance, regardless of Affinity. This holds even with matching Affinities, but the behavior is a little different.

Typically, when a new Task has been launched a new window shows up in the app switcher on the device, like the screenshot below:

![example of task switcher showing multiple tasks](https://cdn.hashnode.com/res/hashnode/image/upload/v1699302594766/6d14bb3b-bb66-48fd-a567-235aa3d03a57.png?height=600)

However, if we try and launch a `singleInstancePerTask` Activity and there's an existing Task that shares the same Affinity then the system hides the original Task, and we only see one entry in the app switcher.

In the diagram below we start with one Task (Task #1) with an original state of A -> B.<sup>1</sup> `ActivityE` is started using the `singleInstancePerTask` Launch Mode and `.MyAffinity` which results in a Task #2 being created.<sup>2</sup> `ActivityF` is started using the `singleInstancePerTask` Launch Mode and `.MyOtherAffinity` which results in Task #3 being created.<sup>3</sup> `ActivityA` and `ActivityB` are started in Task #2 and placed on the top of the stack.<sup>4</sup> Next, `ActivityD` is started using `singleInstancePerTask` and `.MyAffinity` which results in Task #4 being created. Since Task #2 is also already using `.MyAffinity`, Task #2 is hidden.<sup>5</sup> Lastly, `ActivityC` is started using `singleInstancePerTask` and the default Affinity which results in Task #5 being created. Since Task #1 is also using the default Affinity, Task #1 is hidden.<sup>6</sup>

![example of singleinstancepertask behavior when sharing affinity with another task](https://cdn.hashnode.com/res/hashnode/image/upload/v1699394190694/4f086766-7d58-4781-9233-aaa9c25c92fd.png)

To replicate this in the example project start by setting `ActivityC`, `ActivityD`, `ActivityE`, and `ActivityF` as follows:

```xml
<activity  
    android:name=".launchactivities.ActivityF"  
    android:taskAffinity=".MyOtherAffinity"  
    android:launchMode="singleInstancePerTask"  
    android:exported="false" />  
<activity  
    android:name=".launchactivities.ActivityE"  
    android:taskAffinity=".MyAffinity"  
    android:launchMode="singleInstancePerTask"  
    android:exported="false" />  
<activity  
    android:name=".launchactivities.ActivityD"  
    android:taskAffinity=".MyAffinity"  
    android:launchMode="singleInstancePerTask"  
    android:exported="false" />  
<activity  
    android:name=".launchactivities.ActivityC"  
    android:launchMode="singleInstancePerTask"  
    android:exported="false" />

```

Starting Activities A -> B will get us to the original state in the diagram.

`adb shell dumpsys activity recents` output:

```bash
Recent tasks:

Recent #0: Task{5936bd5 #103 type=standard A=10193:com.nicholasfragiskatos.androidlaunchmodes

...

affinity=10193:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{68ab88c u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA t103}, 
ActivityRecord{aa8eee0 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB t103}
]

```

Then after launching `ActivityE` and `ActivityF` we have 3 tasks total.

`adb shell dumpsys activity recents` output: Recent tasks:

```bash
Recent #0: Task{d80e152 #105 type=standard A=10193:.MyOtherAffinity

...

affinity=10193:.MyOtherAffinity

...

Activities=[ 
ActivityRecord{f68d1dd u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityF t105} 
]

Recent #1: Task{ba9599a #104 type=standard A=10193:.MyAffinity}

...

affinity=10193:.MyAffinity

...

Activities=[ 
ActivityRecord{7d25e45 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityE t104} 
]

Recent #2: Task{5936bd5 #103 type=standard A=10193:com.nicholasfragiskatos.androidlaunchmodes

...

affinity=10193:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{68ab88c u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityA t103}, 
ActivityRecord{aa8eee0 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityB t103}
]

```

After launching `ActivityD` we see a new Task created, as expected, to house `ActivityD`. We also notice that the previous Task with `.MyAffinity` (`id=104`, has `ActivityE`) is no longer in the recent Task list, but it does show up in an extra line above the recent tasks list as part of an `mHiddenTasks` output value.

`adb shell dumpsys activity recents` output: Recent tasks:

```bash
mHiddenTasks=[
Task{ba9599a #104 type=standard A=10193:.MyAffinity}
]

Recent tasks:

Recent #0: Task{4a12fcd #106 type=standard A=10193:.MyAffinity

...

affinity=10193:.MyAffinity

...

Activities=[ 
ActivityRecord{815b164 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityD t106}
]

```

Likewise, after launching `ActivityC`, a new `singleInstancePerTask` Activity is created with the default Affinity, and now the previous Task with the default Affinity (`id=103`) is added to the hidden Task list.

`adb shell dumpsys activity recents` output: Recent tasks:

```bash
mHiddenTasks=[
Task{ba9599a #104 type=standard A=10193:.MyAffinity}, 
Task{5936bd5 #103 type=standard A=10193:com.nicholasfragiskatos.androidlaunchmodes}
]

Recent tasks:

Recent #0: Task{5ece29 #107 type=standard A=10193:com.nicholasfragiskatos.androidlaunchmodes}

...

affinity=10193:com.nicholasfragiskatos.androidlaunchmodes

...

Activities=[ 
ActivityRecord{2bf9fb0 u0 com.nicholasfragiskatos.androidlaunchmodes/.launchactivities.ActivityC t107}
]

```

## Conclusion

The Android framework allows the developer to customize how the system manages the relationships between Activities, Tasks, and its Back Stack.

The main goal of this article was to review how we can utilize different Launch Modes to manipulate the Task-Activity relationship, but before that, we needed to lay down some groundwork. First we learned about Tasks and Affinities, and how they are related to Activities. Then we learned what an Affinity is and how manipulating an Activity's Affinity can alter how an Activity associates with a particular Task. Lastly, to help facilitate our understanding we utilized ADB to show the current state of Tasks on a device.

With the groundwork in place, we were able to dive into manipulating an Activity's Launch Mode in its manifest declaration. Each `<activity>` tag supports a `launchMode` attribute that has five different valid values: `standard`, `singleTop`, `singleTask`, `singleInstance`, and `singleInstancePerTask`. We studied each Launch Mode in detail and saw how they behave in theory and in a real demo application. While many of the Launch Modes shared behavior with one or more of the other Launch Modes, they all provided their own unique behavior.

* * *

If you noticed anything in the article that is incorrect or isn't clear, please let me know. I always appreciate the feedback.