@TryLoveCatch
2022-04-26T16:29:31.000000Z
字数 3806
阅读 1434
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()
。