System Tasks and Back stacks

Introduction:

In this document I’m not gonna look at Activity Launch mode and Back stacks as an ordinary way because they have been already well described in Android official documentation, and there are a lot of examples  and illustration on web.Instead I want to unfold some Internal mechanism and hidden aspects of Tasks Stack and Back stacks and their maintaining in system ,which the understanding help a lot on how We should use Launch mode and manage Back stacks.


How stacks work

When talking about Android Back Stacks we may refer many times to Home Screen or Launcher , so to better understand we beging this topic with a brief introduction of Launcher and how it’s playing the role in Backstacks.

Launcher is the first Activity started after you turn on/Restart your device , it’s the root parent of all Activites you run on your smartphone .you start your application from Launcher , Launcher is a simple application like others nothing more ,but with a special purpose that is to allow us to run and navigate through all applications, It lists all installed applications and widgets and you can browse or swipe the list of apps  , when you press the Home Button from anywhere you return back to your Launcher , that’s why Launcher is also called Home Screen Application , so keep in mind Launcher ‘s MainActivity is the Root Activity of all Backstacks on your device, that is why in every Backstacks  when you press back repeatedly you end up navigation in Home Screen.The following diagram show the relationship between Launcher and other Activities:


Don’t confuse Tasks Stack and Back Stack :

Backstack is the stack of  Activities for a single task in which you can navigate from one Activity to another , for example suppose you run your app for the first time , system create a task and put  your MainActivity inside it , then you can run a bunch of Activites like A , B,C,D from it , by default system create for you a BackStack containing MainActivity-A-B-C-D-etc   , when you press back button in C, you go to B :

TasksStack : is the stack of all Tasks that system mange to keep track of all tasks(from different Applications) currently running on your device, you can see it by keeping Home Button (in old devices) or tap on TaskManager button (new devices) , then all currently running Tasks will be show up .


  • Tasks Stack in Relation with Back Stack:


Back Stack:

You can imagine a BackStack like a list of activities that system manage and Developer can define the order and some other behaviour in both code and Manifest.
System identify each stack by an ID (
satckID) and identify each Activity by a Token . Then It creates a list called StackInfo to track and manage Backstacks.
BackStack works as an ordinary Stack based on 
pop and push , peek.....

public class BackStack {

    private List stackInfoList = new ArrayList<StackInfo>();

    public StackInfo pop(){

        return topElement;

    }

    public void push(StackInfo stackInfo){

        stackInfoList.add(stackInfo);

    }

    //other stuff , peek, search ,....

}


By psudo code we can imagine:

StackList backStack = StackList(Activity)//LIFO =Last In First Out

backStack.pop() => Back button press

backStack.push()=>startActivity called

When you call startActivity , system invoke push() and when you press Buck button , pop() will be invoked. System get a pointer to this BackStack for each Activity throgh

StackInfo is a data structure containing information about the current stack , it’s created from Stack ID by ActivityManagerNative and below you can see how is implemented.



Each Activity is assigned a stack ID in creation time, it’s the ID of stack it belong to. ActivityManagerNative is in charge of this ID along with other information .

Notice : ActivityManagerNative is a hidden API ,used only internally , you cannot access its service directly , instead you can use ActivityManager to use available exposed API.

ActivityManagerNative is a singleton object that return all internal information about Activity including its stack ,below is the portion of code taken from SDK implementation of Activity class ,returning stackID the Activity belong to:

//portion of code taken from SDK implementation of Activity

class Activity{

    @Override

    public int getWindowStackId() throws RemoteException {

        return ActivityManagerNative.getDefault().getActivityStackId(mToken);

    }

}


Given stackID , ActivityManagerNative create StackInfo for Activity , the code below taken from ActivityManagerNative shows how StackInfo is made up :

(As you see ActivityManagerNative uses parcelable to manage StackInfo)

class ActivityManagerNative{

    @Override

    public StackInfo getStackInfo(int stackId) throws RemoteException

    {
              Parcel data = Parcel.obtain();
             Parcel reply = Parcel.obtain();
             data.writeInterfaceToken(IActivityManager.descriptor);
             data.writeInt(stackId);
             mRemote.transact(GET_STACK_INFO_TRANSACTION, data, reply, 0);
             reply.readException();
             int res = reply.readInt();
             StackInfo info = null;

        if (res != 0) {

            info = StackInfo.CREATOR.createFromParcel(reply);

        }

        data.recycle();

        reply.recycle();

        return info;

    }

}


Tasks Stack:
Tasks stack as we’ve seen is the list of all back stacks in current device , you can simplify it by this pseudo code:

List TaskStack = new ArrayList<Task>() 

Each Task is mapped to a back stack. 


Every Task in system has a its own Back Stack with the same lifecycle ,if Task is destroyed its attached BackStack is destroyed as well , but if Backstack is empty It doesn’t follow destruction of Task, in this case Back stack may be re-created .Below is the class of each Task which keep a reference to Back stack by stackId member:

public static class RunningTaskInfo implements Parcelable {

        public int id;//A unique identifier for this task.
        public int stackId;
        public ComponentName baseActivity;
        public ComponentName topActivity;
        public Bitmap thumbnail;
        public CharSequence description;
        public int numActivities;
        public int numRunning;
        public long lastActiveTime;
}

Take a look at class you will see StackInfo is declared as an inner static class in RunningTaskInfo

public static class RunningTaskInfo {

  public int id;//A unique identifier for this task.
  public int stackId;
  public ComponentName baseActivity;
  public ComponentName topActivity;
  public Bitmap thumbnail;
  public CharSequence description;
  public int numActivities;
  public int numRunning;
  public long lastActiveTime;

    //StackInfo inner static class
 public static class StackInfo implements Parcelable {

        public int stackId;
        public Rect bounds = new Rect(); //used by Task Manager
        public int[] taskIds;
        public String[] taskNames;
        public Rect[] taskBounds;
        public int[] taskUserIds;
        public ComponentName topActivity;
        public int displayId;
        public int userId;
        public boolean visible;
        public int position;

        //the Parcelable stuff

      .....

    }

}


Tasks are in turn managed by ActivityManager as a class: RunningTaskInfo , Android take care of the life cycle of Tasks according to tasks requirement and memory availability , requirement for a Task is linked to the Acttivity requirement which can be exposed by Manifest or Intent Flag .


How to get the list of current Tasks:
In pre-Lollipop devices It was too simple to get this list ,Just by declare GET_TASKS   permission in Manifest then call getRunningTasks in Activity Manager 

In manifest:

<uses-permission android:name="android.permission.GET_TASKS" />


@Deprecated
public List<RunningTaskInfo> getRunningTasks(int maxNum)
        throws SecurityException {
    try {
             return ActivityManagerNative.getDefault().getTasks(maxNum, 0);

    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();

    }

}



But as you see it’s now deprecated and in post-Lollipops you cannot anymore use this API ,  GET_TASKS   permission is now highly restricted .You can imagine a TaskStack like a List or array system manage but you cannot define order or modify it , it only can be managed at system level , from  API level 21, Marshmalo and later you cannot even see this list like before ,  GET_TASKS is not granted by system until user explicit  action.

Here what Official Android Documentation published:

"This method was deprecated in API level 21.
As of LOLLIPOP, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak personal information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks (though see getAppTasks() for the correct supported way to retrieve that information), and possibly some other tasks such as home that are known to not be sensitive.


Managing Back Stacks:
Back stacks in Android are controlled in two ways ,by  Manifest or Intent’s flag or a combination of both , this variety of controlling channel make things more complex ,in fact combination of flags with a large number of flags while playing with another config file (manifest) require a lot of Check and Balance by developers to reach what they desire and that’s the worth solution in programming I hope one day Google comes with a better concept. 

By LaunchMode in manifest ( 4 modes for Activity)

  1. 1.    Default (standard)
  • OS <5 (pre-Lollipop)
    When we start an Activity , It will placed at top of the current stack (caller stack) , multiple start results in multiple instance.

If such an Activity is outside (another app) It will be placed to our stack.so when back is pressed it return to our app backstack.

  • OS>=5 (post-Lollipop)
    The same as pre-Lollipop as long as we are in the same task, the only difference is for an Activity outside(another app) , instead of placing it in our stack (caller) It create a new Task for that

IN BOTH CASE EACH STARTACTIVITY CREATE A NEW INSTANCE AND NEVER OTHER ACTIVITIES IN STACK DESTROYED BY CALLING NEW ONE.

  1. 2.    SingleTop

Singletop is only effective when the Activity we want to start is currently at top of stack , otherwise it’s absolutely acting as default (standard) mode, if Activity is already at top of stack ,instead of re-creating it , It’s invoked by onNewIntent and stay at top.

  1. 3.    SingleTask

It means system keeps only one instance of Activity on your device (in All tasks) ,this Activity is seen by system as a singleton object , so if the Activity already exists it clear all activities above it then push it to top of current stack.It receive intent through onNewIntent in this case.If it deesn’t exist it create a new one and push to top of stack.It receive intent through onCreate in this case.
Because of Singleton nature of Activity in system, if the Activity already exists in another application , if we start it from our app , the whole stack of this Activity in other application will be brought to us and all Activities
above it will be destroyed.

  1. 4.    SingleInstance

The same as singleTask system has a singleton over it ,except it should be the only Activity in its stack, so pressing Back button results to exit from its current task, and gets back to Home Screen (Launcher).If a SingleInstance Activity start another Activity , a new task will be created because two Activities cannot be together in the same stack, this behaviour may create unexpected situation in some cases that may leads to many problems hard to detect , so you should be careful using them, for example when you’re planning to create a host Activity for App Widgets , you cannot declare your Activity as SingleInstance because App Widgets sometimes run an editing or setting Activity that can be done only in the context of current task which is in conflict with SingleInstance  not allowing another Activity in the current task so app raise host exception. (image below)



By Flag in Intent:
In addition to Manifest you can also apply some behaviour of your Activity by  intent flags such as  FLAG_ACTIVITY_NEW_TASK ,FLAG_ACTIVITY_SINGLE_TOP or FLAG_ACTIVITY_CLEAR_TOP.


TaskStackBuilder :
This is a new and very interesting API came from API21 , It create a fake Back Stack , and let you to manage it.Fake because It’s not managed the same way as other back stacks , It’s just based on some sort of indexing mechanism. TaskStackBuilder  is especially interesting when you start an Activity from Notification, for example suppose you have a Messaging Application and you receive a new message from Notification , by tapping you want to open an Activity which show New Message, if you press Back it exits from Activity and you come back to Home Screen , but you don’t like this behaviour and you like user be able to get into Message List when pressing Back button, in this case you  create a Back Stack by TaskStackBuilder  then you define your activities to add to stack.

 

First declare your parent for your Activity in manifest:

<activity

    android:name=".ChildActivity"

          android:parentActivityName=".ParentActivity">

       <meta-data

         android:name="android.support.PARENT_ACTIVITY"

         android:value=".ParentActivity"/>

</activity>

 

in our case for NewMessageActivity becomes like this:

<activity

    android:name=".NewMessageActivity"

          android:parentActivityName=".MainActivity"> 

       <meta-data

         android:name="android.support.PARENT_ACTIVITY"

         android:value=".MainActivity"/> 

</activity>

 

Then you build your TaskStackBuilder as following:

Intent newMessageIntent = new Intent(this, NewMessageActivity.this);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

//You add your intent:
stackBuilder.addNextIntentWithParentStack(newMessageIntent);

// get pending intent:
PendingIntent pendingIntent = stackBuilder .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

//passe intent to your Notification builder
notification.setContentIntent(pendingIntent);

 


May 2017 
Khosrov


 © Xosrov 2016