Playing with Android Task Affinity and Launch Modes

Veeresh Charantimath
4 min readMar 2, 2019

--

A special requirement came in where I needed to keep this SuperImportantActivity alive always for the user to return to at any given point and not lose its state (unless he/she explicitly wants to), keeping in mind that there are other activities also part of this app.

A quick read of the developer docs suggests setting the flag FLAG_ACTIVITY_CLEAR_TOP before the startActivity call to the SuperImportantActivity, which solves half the problem because if there are any activities on top of the SuperImportantActivity, they are cleared, which is not I wanted.

Another approach is to use two stacks where the first will house only the SuperImportantActivity and the other will house the rest of the activities of the app.

If you have ever used finishAffinity() you will notice that the activity stack is cleared. By default, all activities in your app have an affinity (a natural liking) to the package name of your app, thus the behavior.

Now, to have my SuperImportantActivity have a different affinity, which in turn places it in a different stack we use a property called taskAffinity of the <activity> element

Caution — When starting the SuperImportantActivity if you don’t set the Intent.FLAG_ACTIVITY_NEW_TASK flag with the start activity call it won’t be started in a new stack, but rather launched in the same stack even if you have set a different taskAffinity in your manifest.

Now, printing our back stack after making the call to the SuperImportantActivity looks like this

Use this command the print the backstack

adb shell dumpsys activity activities | sed -En -e ‘/Running activities/,/Run #0/p’

Clearly, SuperImportantActivity is placed in a different stack named superImportant.container and the other stack where the rest of the activities live points to the package name of the app.

Now, an important thing to know is that if you launch an activity, it will have an affinity to the current stack.

Example, if I launch Activity2 from my SuperImportantActivity, the former will be a part of superImportant.container stack as shown below

Caution — If any stack is left unvisited for a long time by the user, Android tries to clear all activities of the stack to claim resources. Using

alwaysRetainTaskState

will not clear the stack even left unvisited for a long time.

Now, to switch between stacks i.e to launch MainActivity which is the other stack we launch it using a PendingIntent with the intent having the following flags

Intent.ACTION_MAIN

Intent.CATEGORY_LAUNCHER

this emulates the user pressing the recents tab and doing a switch between the stack. This is important to maintain the state of the activity i.e without launching a new instance or clearing anything on top of it.

If you don’t wish to return to where you exactly left, you can bring to front a particular activity from the stack using the flags from below from the current stack and get rid of the PendingIntent. This is what I do to for launch SuperImportantActivity which is in another stack

Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP

Our app (its an Android TV app) runs on a custom Android ROM implementation where recents screen is not shown(just the UI) to the user but on the other hand, stock Android ROM show each stack as an app to the user and there is a chance the user may clear recents screen.

When in doubt use the adb command to check the backstack.

Improvements and alternatives are always welcomed, thanks!

--

--