[关闭]
@TryLoveCatch 2022-04-26T16:29:31.000000Z 字数 3806 阅读 1415

Android知识体系之启动模式

Android知识体系


任务栈 Task

任务栈,可以理解为一个盛放Activity的容器,而且很明显,是一个栈,先入后出。
我们在启动一个App的时候,系统会先给它创建一个任务栈,然后放入MainActivity。然后每次启动一个新的Activity,就会再次压入这个栈里面。
所谓出栈,当我们单机back键的时候,就会有一个Activity出栈,当栈里面没有Activity的时候,系统就会回收这个任务栈。

standard

标准模式,也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在。这是一种典型的多实例实例,一个任务栈可以有多个实例。

案例

AB都是standard模式,在A中启动B,然后B再启动A,这个时候,任务栈就应该是ABAB,如下图所示:

应用内调用

standard模式下,谁启动了这个Activity,那么它就运行在启动它的那个Activity的所在的栈中。

跨进程调用

5.0之前,和应用内调用一样,会出现在调用者所在栈的顶部。
5.0之后,会新建一个Task,新启动的Activity会放入这个新的Task中。

扩展

相信大多数人都应该遇到过,使用ApplicationContext来启动一个Activity,如果这个Activitystandard模式,那么,就会报错,如下:

  1. 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?

对于这个错误,大家应该都不陌生,解决方法也非常简单:

  1. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

加上一句话就能解决,但是为什么会出现这个错误呢?
就跟standard模式有关,我们上面说过

standard模式下,谁启动了这个Activity,那么它就运行在启动它的那个Activity的所在的栈中。

但是非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈。所以相对应的,解决方式就是指定FLAG_ACTIVITY_NEW_TASK标记位,启动的时候,先创建一个新的任务栈,然后将启动的Activity放入进去。

singleTop

栈顶复用模式。在这种模式下,如果新的Activity已经位于任务栈的栈顶,那么Activity不会重新创建一个实例,而是会调用已存在的Activity实例的onNewIntent()
如果新的Activity已经存在但不在栈顶,或者不存在,那么都会重新新建一个实例。
也就是说,除了位于栈顶,其他的和standard一样。

案例

任务栈T1中,有ABCD四个ActivityA位于栈底,D位于栈顶

应用内调用

谁启动了这个Activity,那么它就运行在启动它的那个Activity的所在的栈中,并且会在这个栈里面,来判断是否位于栈顶。

跨进程调用

5.0之前,和应用内调用一样,会出现在调用者所在栈的顶部。
5.0之后,会新建一个Task,新启动的Activity会放入这个新的Task中。

singleTask

栈内复用模式。在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,只会调用onNewIntent()
如果A的启动模式是singleTask,启动A之后,系统首先会去寻找A想要的任务栈,如果不存在,就创建一个任务栈,然后创建A的实例放到栈中。如果A所需的任务栈存在,这时,要看该任务栈中是否存在A的实例,如果实例存在,那么系统会将A上面的所有Activity全部出栈,这样A就位于栈顶了,并且调用A的onNewIntent();如果实例不存在,就新建一个A的实例并压入栈中。
singleTask默认带有关clearTop的效果。

案例

任务栈T1的情况为ABC,这个时候DsingleTask模式启动

taskAffinity

任务相关性。上面我们一直在说

所需的任务栈

那么什么是所需的任务栈呢?
这就涉及到taskAffinity了。这个参数是Activity所需要的任务栈的名字。
默认情况下,所以Activity所需的任务栈都是应用的包名,我们可以通过taskAffinity这个属性来指定单独的任务栈,只要名字和应用的包名不一样即可。

taskAffinity属性,主要跟singleTaskallowTaskReparenting属性来配对是使用,其他情况下没有任何意义。

singleTask配合使用,taskAffinity的属性值就是具有singleTask模式的Activity的目标任务栈。启动该Activity,它会创建并且运行在名字和taskAffinity相同的任务栈中。如果没有指定taskAffinity,就是默认的任务栈,即应用的包名。

应用内调用

如上例子所说

跨进程调用

同应用内调用

扩展

A启动模式是standardBC启动模式都为singTask,并且taskAfinity都为com.test.task2,那么,A->B->C->A->B,如此的启动顺序,现在的情况是什么样的?

A->BBsingTask,并且指定了taskAfinity,所以会新建B所需的任务栈T2,并新建B的实例放进去。这个时候两个任务栈 T1[A], T2[B]
B->CCsingTask,并且指定了taskAfinity,由于和BtaskAfinity相同,所以是同一个任务栈T2,这个时候T2已经存在,只需要新建C的实例放进去。这个时候,T1[A], T2[C, B]
C->AAstandard,谁启动了它,它就放在启动者的任务栈里面,所以会新建A的实例,并放入T2。这个时候,T1[A], T2[A, C, B]
A->BBsingTask,所以栈内唯一,并且再带clearTop效果,而且所需的栈和实例已经存在,不会重新创建,所以,A, C被出栈,B回到栈顶。这个时候,T1[A], T2[B]

singleInstance

单实例模式。这种模式的Activity会单独的位于一个任务栈里面,只要它存在,就不会新建实例,只会调用onNewIntent()

这个任务栈比较特殊,只会唯一存在该Activity。也就是说,它启动了其他Activity,被启动的会放入另外的任务栈里面;任何Activity启动了它,它都会处于自己的任务栈里面,不同于启动者。

Flags

参考: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

参考

我打赌你一定没搞明白的Activity启动模式
深入讲解Android中Activity launchMode

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注