Popping The Stack in Android

http://justmobiledev.com/wp-content/uploads/2018/12/android-popping-stack-4.jpghttp://justmobiledev.com/wp-content/uploads/2018/12/android-popping-stack-4.jpghttp://justmobiledev.com/wp-content/uploads/2018/12/android-popping-stack-4.jpgPopping The Stack in Android

Working with Android Tasks and Activities can be confusing to a developer and can cause a great deal of frustration. This Post is intended to review the different concepts and shed some light on to how they work together and can be controlled.

Activities and Fragments

Activities are the main building blocks of an application. The Android documentation defines them as a single focused task the user can work on. Together with a corresponding layout, the Activity appears as a page to the user.

Fragments are further segmentations of the UI into smaller components for the purpose of reusability and maintainability. They can be used to organize a complex UI page into sections that can be controlled separately. Fragments are attached to Activities and managed using a FragmentManager. For the purpose of this tutorial, we will be disregarding fragments.

Understanding Tasks

Tasks in Android are are a collection of Activities in order to group them for a purpose, e.g. to align the activities with the same functional purpose or to structure your navigation flow.

Activities in a Task are organized in a stack – a sequence of Activities piled on top of each other – in the order the Activities were opened.

When the user continues hitting the ‘Back’ button, and all Activities are removed from Task ‘A’, this task is destroyed and Activities are now popped off the stack in Task ‘B’.

Task Stack Lifecycle

Let’s review the steps of what happens when an app is launched and Activities are added and removed:

  1. The application launcher creates a new Task with the main Activity created and placed in the bottom (root) of the stack. The main Activity is identified with the android.intent.action.MAIN action intent filter and category of android.intent.category.LAUNCHER in the Android manifest.
  2. From the main Activity, another Activity is started and pushed on top of the stack. The new Activity moves into the foreground.
  3. When another Activity is started, the previous Activity moves onto the back stack and is paused.
  4. When the Back button is pressed, the current Activity is popped from the top of the back stack. This destroys the Activity. The Activity below in the stack is resumed and moves into the foreground.
  5. The Back button can be used to keep popping Activities from stack. Once the last activity is popped off, the home screen is displayed.
  6. Once a Task is empty, it can be destroyed by the system.

You can see a list of recently used Tasks in the Android Recents screen.

For more details, Android documentation has an excellent overview here.

When to start a new Task

Starting a new Task is a good navigation strategy if you want to break the ‘Back’ button flow. For example, after a user logs in to your application, you don’t want the user to see the Login page again by pressing the ‘Back’ button. Instead, you want to finish the Login Activity Task and start the main post-login page on a new Task.

This can be achieved by two means:

  1. Using Android Manifest tag with attribute android:launchMode.
  2. Including Flags in the intent delivered to the startActivity().

An excellent tutorial about launch modes and start flags is here.

Launch Modes

The following launch modes can be defined in the Android manifest <activity /> tag:


1
2
3
4
5
 &lt;activity
    android:name=".YourActivity"
    android:launchMode="singleTop"
    ...
    />

standard:
This is the default mode. In case you don’t define any launchMode, the new activity is launched in standard mode.  The standard launch mode means that a new Activity instance will be created whenever you start a new Activity. (stack example: [A – B – A – C])

singleTop:
Before creating a new Activity, the system will check whether this activity is already on the top of the Activity Stack. If ‘yes’, then it won’t create a new instance of the Activity but instead it will call the onNewIntent() method of the same Activity. (stack example: [A – B – A] -> new A calls onNewIntent() of top A instance)

singleTask:
When you start a new Activity will be launched in the new Task, if you define a different taskAffinity than the standard task affinity. If not, the Activity will still be created in the default Task. (stack example: [A, B] -> [C, C] (C created in new Task)

singleInstance:
This launch mode behaves like ‘singleTask’ with the only difference that this Activity can be the only Activity in the task. (stack example:
A, B] -> [C] C created as single instance in new Task)

Intent Flags

FLAG_ACTIVITY_NEW_TASK:

This flag is the corresponding flag to the launch mode ‘singleTop’. When you start a new Activity will be launched in the new Task, if you define a different taskAffinity than the standard task affinity. If not, the Activity will still be created in the default Task.


1
2
3
Intent i = new Intent(FirstActivity.this, SecondActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);

FLAG_ACTIVITY_SINGLE_TOP:

This flag corresponds to the ‘singleTop’ launch flag. Before creating a new Activity, the system will check whether this activity is already on the top of the Activity Stack. If ‘yes’, then it won’t create a new instance of the Activity but instead it will call the onNewIntent() methodof the same Activity


1
2
3
Intent i = new Intent(FirstActivity.this, FirstActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);

FLAG_ACTIVITY_CLEAR_TOP:

There are two scenarios that are important to understand:
1. All Activities reside in the same Task:
In this case all Activities will be cleared from the top of the stack and the new Activity will be brought into the foreground.
2. Activities are scattered between several Tasks:
In this case Android will bring the required Task that contains the intended Activity to Foreground, and clear all the Activities on the top of that Activity.


1
2
3
Intent i = new Intent(FirstActivity.this, FirstActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);

How to start a new Task

So how can you start a new task for sure? You can start a new Task when creating the Intent to start a new Activity by using the following flag:


1
2
3
Intent intent = new Intent(ActivityCTask2.this, ActivityATask2.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

However, as discussed above this does not always guarantee that the system will create a new task. The system looks for a different task to place the new activity in. Often, it’s a new task. However, if there’s already an existing task with the same affinity as the new activity, the activity is launched into that task. If not, it begins a new task.

What is Task Affinity?

Task Affinity is the preferences that a Activity has to belong to a certain Task. For example, by default Activities have an affinity towards the app package name, so they are created in the same Task.

If you want to force the system to create a new Task, you need to specify the taskAffinity for the corresponding Activity in the Android Manifest.

You can control the Task Affinity in the Android Manifest using:


1
2
3
&lt;activity
 android:taskAffinity="&lt;new task package name, e.g. com.justmobiledev.androidpoppingstack1.new_task>"
/>

Popping the Stack: Implementation Options

In this section I wanted to throw out a few suggestions how popping the stack could be implemented, so how to remove all Activities from the Task or Tasks and show the first Activity on the bottom of the Task stack.

onResume() Handler

The first approach that can be used to pop all Activities back to the stack is to implement an onResume() event handler in the each Activity. The onResume() event of the Activity life cycle get’s triggered when an Activity comes into the foreground.

We can use the onResume() method to close the Activity itself under the condition that we want to pop the stack. The top-level Activity would issue the command to pop and close itself. Then the next Activity underneath would come into the foreground, trigger onResume() and figure out it needs to close itself. One after the other, Activities on the stack will close themselves.

Let’s start by creating a BaseActivity, so the onResume() handler doesn’t need to be implemented in all Activities.


1
2
3
4
5
6
7
8
9
10
11
12
ppublic class BaseActivity extends AppCompatActivity {
    public static boolean popStackFlag = false;

    @Override
    protected void onResume() {
        super.onResume();

        if (popStackFlag){
            finish();
        }
    }
}

As you can see, we are using a static member of BaseActivity called ‘popStackFlag’ to determine if the Activity should finish. All of your Activities should now be sub-classed of BaseActivity.

Now, if you want to pop the stack, all you have to do in the top-level Activity is:


1
2
popStackFlag = true;
finish();

In order to prevent that the bottom-level Activity closes itself, you have two options:

  1. Don’t sub-class it off BaseActivity
  2. Turn the popStackFlag off before you call the onResume() super method as shown below.

1
2
3
4
5
    @Override
    protected void onResume() {
        popStackFlag = false;
        super.onResume();
    }

Pro:
– Relatively little implementation effort.
– Works across Tasks
– Does not recreate the bottom stack Activity
Cons:
– Not the standard way of ‘doing things’ in Android.
– All Activities have to be sub-classed of a common parent, unless you want to implement the handler in each Activity.

Using FLAG_ACTIVITY_CLEAR_TOP

We already discussed the FLAG_ACTIVITY_CLEAR_TOP Intent flag when starting an Activity. It means when starting an Activity, if the Activity that you are about to start already exists lower in the stack, then instead of creating a new instance of the Activity, all Activities in between will be closed and the Activity below will be moved into the foreground.

This is the standard Android way of pushing the stack: By opening an Activity that’s below the top with the CLEAR_TOP flag.

So how does this work if you have a stack that is A B A C and you want to push back to the lowest instance of A? Clear Top would still work in this case.

In order to implement this in the top-level Activity, all you need to do is create a new intent with the target of the lowest Activity and set a flag of FLAG_ACTIVITY_CLEAR_TOP, then start that new Activity and finish the current one.


1
2
3
4
5
6
Intent intent = new Intent(ActivityC.this, ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

// Finish current activity
finish();

Pro:
– Preferred way of ‘doing things’
– Little implementation effort
– Works across Tasks
Cons:
– Recreates the target Activity
– The target Activity has to be below in the stack. You can’t clear the top and create a completely new Activity.

Using Broadcast Receivers to pop the Stack?

Implementing Broadcast Receivers for popping the stack of activities won’t work, because Activities that are below the top Activity are in ‘Paused’ state and thus cannot receive Broadcasts. Only Activities that are in ‘Started’ state can receive Broadcasts.

Back Stack cleared by the System

One situation to be aware of is that if the user leaves a task running for a long time, the system may clear the task of all activities, exception the root activity. (see Android documentation)

When the user starts interacting again, these previous Activities are not restored again (because the system assumes the user has abandoned whatever they were doing before).

You can prevent this by setting the following property in the Android manifest for the root Activity. This causes the task to retain all activities in its stack even after a long period.

alwaysRetainTaskState = true

Sample App: Stack Playground

If you would like to play with Tasks and Activities, check out my Playground app on my GitHub repository here.

It let’s you create an Activity Stack with one Task and Two Tasks and you can see the tasks and bottom/top activities listed.

You can play with the Back button and check out the Recent lists to see the Tasks created.

Author Description

justmobiledev

No comments yet.

Join the Conversation