AMS-Task和LauncherMode

0

前言

如何才能开始一个新的Task?
Intent中定义了一个标志FLAG_ACTIVITY_NEW_TASK,在startActivity的Intent参数中加入该标志就能开启一个新的Task。但是,如果系统中已经有相同affinity的Task存在,这时候就不会再启动一个Task,而是将旧的Task带到前台。
Affinity的意思是“亲和度”、“密切关系”,它的类型是字符串,我们可以把它理解成Task的名称。Affinity字串在系统中是唯一的,AMS查找一个Task,最优先比较它的affinity。
ActivityStack类中用来查找Task的方法是findTaskLocked()

findTaskLocked

    ActivityRecord findTaskLocked(ActivityRecord target) {
        ......
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; -
                -taskNdx) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            ......
            if (!isDocument && !taskIsDocument && task.rootAffinity
                    != null) {
                if (task.rootAffinity.equals(target.taskAffinity)) {
                    return r;
                }
            } else if (taskIntent != null && taskIntent.getComponen
            t() != null &&
                    taskIntent.getComponent().compareTo(cls) == 0
                    &&
                    Objects.equals(documentData, taskDocumentData))
            {
                return r;
            } else if (affinityIntent != null && affinityIntent.get
            Component() != null &&
                    affinityIntent.getComponent().compareTo(cls) ==
                            0 &&
                    Objects.equals(documentData, taskDocumentData))
            {
                return r;
            }
        }
        return null;
    }

findTaskLocked()方法首先遍历已有TaskRecord对象的affinity变量是否等于ActivityRecord的taskAffinity变量,如果相同就直接把旧的Task带回前台,而不是new一个新的TaskRecord。
既然一个Task的affinity这么重要,它是在哪里定义的呢?在AndroidManifest.xml文件中:Activity标签的taskAffinity属性:当使用标志FLAG_ACTIVITY_NEW_TASK启动一个Activity时才起作用
Application标签的taskAffinity属性:没有指定activity标签的taskAffinity属性的,将会继承application标签的taskAffinity属性应用的包名packageName:没有指定的情况,默认值通常在开发中,很少应用会自定义一个taskAffinity属性,所以默认就是其包名。因此,在应用中如果启动本应用的另一个Activity,即便intent里添加了FLAG_ACTIVITY_NEW_TASK也不一定会启动一个新的Task,除非这个Activity定义了不同的taskAffinity属性。

Activity对象的复用

启动一个Activity时,如果系统的后台Task已经有一个该Activity的实例存在,那么系统会再创建一个新的Activity实例,还是将已经存在的Activity实例切换到前台呢?
答案是:都有可能。有很多因素可以影响结果,包括Activity的属性值以及Intent中指定的标志。我们先看看Activity的属性launcherMode会有哪些影响:

standard模式:standard模式下的Activity每次启动时都会创建该Activity的实例对象;

同一个Task中可以同时存在该Activity的多个实例;一个Activity的多个实例可以出现在多个Task栈中。

singleTop模式:如果设置为singleTop模式的Activity实例位于Task的栈顶,则不会创建一个新的对象,但是该Activity对象切换到前台时,它的onNewIntent()方法将会被调用,新的intent通过这种方式传递给实例对象。如果Activity不在其Task的栈顶,就和standard模式一样,会创建新的实例对象。

singleTask模式:设置为singleTask模式的Activity具有系统唯一性,只能在系统中创建该Activity的一个实例对象。启动设置为singleTask的Activity时,如果系统中已经存在该Activity的实例,则将其所在的Task排在它前面的Activity都出栈,将该Activity带到栈顶,并调用onNewIntent()方法,将新的intent传递给该实例。如果该Activity在系统中还没有实例对象,就会创建一个该Activity的实例对象,如果该Activity的taskAffinity属性值和当前Task的affinity值相同,它会加入到当前TAsk中,否则,即使启动该Activity的Intent中没有指定FLAG_ACTIVITY_NEW_TASK标志,也会启动新的Task,将Activity置于其中。

singleInstance模式:设置为singleInstance模式的Activity同样具有系统唯一性,系统
中只有该Activity的一个实例对象。同时Activity位于一个单独的Task中,该Task中也只
有一个Activity。

allowTaskReparenting属性:通常情况下,一个Activity创建出来后,会停留在某个
Task中,直到它被销毁。但是如果Activity的allowTaskReparenting属性设置为true,
则该Activity可以在不同的Task之间转移。但是,这个属性只有在启动Activity的Intent中
设置了FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标志时才起作用。

allowRetainTaskState属性:默认情况下,如果一个Task位于后台的时间太长,系统
会清理该Task中的Activity,除了最初启动的Task的Activity以外,其他的Activity都会被
系统销毁。如果应用希望保留这些Activity,可以将启动Task的Activity的
allowRetainTaskState属性设置为true。

clearTaskOnLaunch属性:前面介绍了,当使用带有标志
FLAG_ACTIVITY_NEW_TASK的Intent启动一个Activity时,如果该Acitivty位于一个
Task中,会将Task整体带到前台,其中Activity保持不变。但是如果该Activity启动的是
Task的根Activity(root Activity),同时该Activity的属性clearTaskOnLaunch设置为
true,那么系统出了将Task带到前台外,还会清除除了root Activity以外的所有
Activity。因此,这个属性的作用相当于每次销毁Task,然后重新开始一个。

finishOnTaskLaunch属性:设置为true,系统将会销毁该Activity,然后重新再启动一
个。

除了FLAG_ACTIVITY_NEW_TASK标志以外,Intent中还定义几个和Activity相关的标志: FLAG_ACTIVITY_CLEAR_TOP:如果启动的Activity已经存在,则把该Activity带到前 台,并把它前面的Activity都出栈。 FLAG_ACTIVITY_BROUGHT_TO_FRONT:如果启动的Activity已经存在,则把该 Activity带到前台,但是不关闭它前面的Activity。 FLAG_ACTIVITY_SINGLE_TOP:如果启动的Activity已经位于Task的栈顶,则不会创 建一个新的Activity,而是把该Activity带到前台。 Android开发——Intent中的各种FLAG