@TryLoveCatch
2022-04-26T08:29:31.000000Z
字数 3806
阅读 1732
Android知识体系
任务栈,可以理解为一个盛放Activity的容器,而且很明显,是一个栈,先入后出。
我们在启动一个App的时候,系统会先给它创建一个任务栈,然后放入MainActivity。然后每次启动一个新的Activity,就会再次压入这个栈里面。
所谓出栈,当我们单机back键的时候,就会有一个Activity出栈,当栈里面没有Activity的时候,系统就会回收这个任务栈。
标准模式,也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在。这是一种典型的多实例实例,一个任务栈可以有多个实例。
A和B都是standard模式,在A中启动B,然后B再启动A,这个时候,任务栈就应该是ABAB,如下图所示:

standard模式下,谁启动了这个Activity,那么它就运行在启动它的那个Activity的所在的栈中。
5.0之前,和应用内调用一样,会出现在调用者所在栈的顶部。
5.0之后,会新建一个Task,新启动的Activity会放入这个新的Task中。
相信大多数人都应该遇到过,使用ApplicationContext来启动一个Activity,如果这个Activity是standard模式,那么,就会报错,如下:
Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
对于这个错误,大家应该都不陌生,解决方法也非常简单:
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
加上一句话就能解决,但是为什么会出现这个错误呢?
就跟standard模式有关,我们上面说过
standard模式下,谁启动了这个Activity,那么它就运行在启动它的那个Activity的所在的栈中。
但是非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈。所以相对应的,解决方式就是指定FLAG_ACTIVITY_NEW_TASK标记位,启动的时候,先创建一个新的任务栈,然后将启动的Activity放入进去。
栈顶复用模式。在这种模式下,如果新的Activity已经位于任务栈的栈顶,那么Activity不会重新创建一个实例,而是会调用已存在的Activity实例的onNewIntent()。
如果新的Activity已经存在但不在栈顶,或者不存在,那么都会重新新建一个实例。
也就是说,除了位于栈顶,其他的和standard一样。
任务栈T1中,有ABCD四个Activity,A位于栈底,D位于栈顶
D的启动模式为standard,这个时候再次启动D,我们知道T1中的情况应该为ABCDD。D的启动模式为singleTop,同样的,再次启动D,这个时候T1中的情况还是ABCD。 
谁启动了这个Activity,那么它就运行在启动它的那个Activity的所在的栈中,并且会在这个栈里面,来判断是否位于栈顶。
5.0之前,和应用内调用一样,会出现在调用者所在栈的顶部。
5.0之后,会新建一个Task,新启动的Activity会放入这个新的Task中。
栈内复用模式。在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,只会调用onNewIntent()。
如果A的启动模式是singleTask,启动A之后,系统首先会去寻找A想要的任务栈,如果不存在,就创建一个任务栈,然后创建A的实例放到栈中。如果A所需的任务栈存在,这时,要看该任务栈中是否存在A的实例,如果实例存在,那么系统会将A上面的所有Activity全部出栈,这样A就位于栈顶了,并且调用A的onNewIntent();如果实例不存在,就新建一个A的实例并压入栈中。
singleTask默认带有关clearTop的效果。
任务栈T1的情况为ABC,这个时候D以singleTask模式启动
D所需的任务栈也为T1,由于T1已经存在了,但是D的实例没有存在,所以系统会创建一个D的实例并压入到`T1中去。

D所需的任务栈为T2,由于T2和D的实例都不存在,所以系统会先创建任务栈T2,然后创建D的实例并压入T2中。

假设T1的情况为ADBC,D所需的任务栈也是T1,以singleTask模式启动D,由于T1已经存在,不会重新创建任务栈,并且D的实例也存在,所以也不会重新创建D的实例,这个时候,B和C会被出栈,然后D就位于栈顶,并调用D的onNewIntent()。

假如目前有两个任务栈,前台任务栈T4的情况为AB,后台任务栈t4里存有CD,假设CD的启动模式均为singleTask,现在由B去启动D,那么整个后台任务都会被切换到前台,这个时候整个后退列表就变成了ABCD。

假如上面的其他条件不变,B启动的是C而不是D,那么整个栈的情况就变成了ABC,因为D在C上面,会被清理出栈。

任务相关性。上面我们一直在说
所需的任务栈
那么什么是所需的任务栈呢?
这就涉及到taskAffinity了。这个参数是Activity所需要的任务栈的名字。
默认情况下,所以Activity所需的任务栈都是应用的包名,我们可以通过taskAffinity这个属性来指定单独的任务栈,只要名字和应用的包名不一样即可。
taskAffinity属性,主要跟singleTask和allowTaskReparenting属性来配对是使用,其他情况下没有任何意义。
与singleTask配合使用,taskAffinity的属性值就是具有singleTask模式的Activity的目标任务栈。启动该Activity,它会创建并且运行在名字和taskAffinity相同的任务栈中。如果没有指定taskAffinity,就是默认的任务栈,即应用的包名。
如上例子所说
同应用内调用
A启动模式是standard,B和C启动模式都为singTask,并且taskAfinity都为com.test.task2,那么,A->B->C->A->B,如此的启动顺序,现在的情况是什么样的?
A->B,B是singTask,并且指定了taskAfinity,所以会新建B所需的任务栈T2,并新建B的实例放进去。这个时候两个任务栈 T1[A], T2[B]。
B->C,C是singTask,并且指定了taskAfinity,由于和B的taskAfinity相同,所以是同一个任务栈T2,这个时候T2已经存在,只需要新建C的实例放进去。这个时候,T1[A], T2[C, B]。
C->A,A是standard,谁启动了它,它就放在启动者的任务栈里面,所以会新建A的实例,并放入T2。这个时候,T1[A], T2[A, C, B]。
A->B,B是singTask,所以栈内唯一,并且再带clearTop效果,而且所需的栈和实例已经存在,不会重新创建,所以,A, C被出栈,B回到栈顶。这个时候,T1[A], T2[B]。
单实例模式。这种模式的Activity会单独的位于一个任务栈里面,只要它存在,就不会新建实例,只会调用onNewIntent()。
这个任务栈比较特殊,只会唯一存在该Activity。也就是说,它启动了其他Activity,被启动的会放入另外的任务栈里面;任何Activity启动了它,它都会处于自己的任务栈里面,不同于启动者。
设置Flags的会覆盖AndroidManifest里面的配置。
在AndroidManifest里面无法设置FLAG_ACTIVITY_CLEAR_TOP标记。而Flags无法指定singleInstance模式
FLAG_ACTIVITY_NEW_TASK
这个标记位 + FLAG_ACTIVITY_CLEAR_TOP,等同于singleTask。
FLAG_ACTIVITY_SINGLE_TOP
这个标记位,等同于singleTop。
FLAG_ACTIVITY_CLEAR_TOP
A的启动模式时standard,启动时设置了这个标记位,那么如果A在栈中存在,A以及A以上的Activity都将出栈,并且新建一个A的实例,放入到栈中。A设置了FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP标记位,那么A以上都将出栈,并且调用A的onNewIntent()。A设置了FLAG_ACTIVITY_SINGLE_TOP和FLAG_ACTIVITY_CLEAR_TOP标记位,那么A以上都将出栈,并且调用A的onNewIntent()。