一、Activity的LaunchMode
1. standard:标准模式,系统的默认模式。
每次启动一个Activity都会重新创建一个新的实例(Activity的onCreate、onStart、onResume方法会被系统调用),不管这个实例是否已经存在。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的任务栈中。
比如,同一应用中,ActivityA启动了ActivityB(ActivityA是standard模式,ActivityB是standard模式),那么ActivityB就会进入到ActivityA所在的任务栈中。
注意:当我们用ApplictionContext去启动standard模式的Activity时,会报如下错误:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
这是因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,而非Activity类型的Context(比如ApplicationContext)并没有所谓的任务栈,因此出问题。
解决这个问题的方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会创建一个新的任务栈,这个时候待启动的Activity实际上是以singleTask模式启动的。
2. singleTop:栈顶复用模式。
在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调(Activity的onPause、onNewIntent、onResume方法会被系统调用),通过此方法的参数我们可以取出当前请求的信息。
比如,同一应用中,ActivityA启动了ActivityB(ActivityA是standard模式,ActivityB是singleTop模式),那么ActivityB就会进入到ActivityA所在的任务栈中,这个时候ActivityB再启动ActivityB,由于ActivityA所在的任务栈中栈顶为ActivityB,因此不会再重新创建ActivityB实例,而是会回调栈顶ActivityB的onNewIntent方法。
3. singleTask:栈内复用模式。
这是一种单实例模式,在这种模式下,系统首先会寻找是否存在Activity想要的任务栈,
(1)如果不存在Activity想要的任务栈,就创建一个任务栈,然后创建Activity实例并放入到栈中。
(2)如果存在Activity想要的任务栈,这时要看任务栈是否存在Activity实例,
(2.1)如果不存在Activity实例,就创建Activity实例并放入到栈中。
(2.2)如果存在Activity实例,系统就会把Activity上面的其它Activity全部出栈,并回调其onNewIntent方法(Activity的onNewIntent、onRestart、onStart、onResume方法会被系统调用)。
比如,同一应用中,ActivityA启动了ActivityB(ActivityA是singleTask模式,ActivityB是standard模式),那么ActivityB就会进入到ActivityA所在的任务栈中,这个时候ActivityB再启动ActivityA,由于ActivityA所在的任务栈中已经存在ActivityA实例,因此不会再重新创建ActivityA实例,而是将栈内ActivityA上面的ActivityB出栈,并回调ActivityA的onNewIntent方法。
4.singleInstance:单实例模式
在这种模式下,Activity实例只能单独地存在于一个任务栈中,后续的请求均不会创建新的Activity实例,除非这个独特的任务栈被系统销毁了。
比如,同一应用中,ActivityA启动了ActivityB(ActivityA是singleInstance模式,ActivityB是standard模式),那么ActivityB就不会进入到ActivityA所在的任务栈中,系统会创建新的任务栈,并创建ActivityB实例放入到新栈中,而ActivityA仍然单独地存在于其所在的任务栈中。
情景分析:
有两个任务栈,前台任务栈1有两个Activity,分别为ActivityA(栈底,standard模式)、ActivityB(栈顶,standard模式),后台任务栈2有两个Activity,分别为ActivityX(栈底,singleTask模式)、ActivityY(栈顶,singleTask模式),分以下两个情况:
情况1. ActivityB去启动ActivityY,
整个任务栈2会被切换到前台,即任务栈2显示在任务栈1上面。连续按返回键则出栈顺序为:ActivityY,ActivityX,ActivityB,ActivityA。
情况2. ActivityB去启动ActivityX,
任务栈2中ActivityY出栈,剩下的任务栈全部被切换到前台,即任务栈2显示在任务栈1上面。连续按返回键则出栈顺序为:ActivityX,ActivityB,ActivityA。
TaskAffinity属性:
可翻译为任务相关性,这个参数标识了一个Activity所需要的任务栈名字,默认情况下,所有Activity所需的任务栈名字为应用的包名,当然,我们可以为每一个Activity都单独指定TaskAffinity属性。
TaskAffinity属性主要和singleTask模式和allowTaskReparenting属性配合使用,在其他情况下没有意义。分以下两个情况:
情况1. 当TaskAffinity属性和singleTask模式配合使用时,
待启动Activity会运行在名字和TaskAffinity相同的任务栈中。
情况2. 当TaskAffinity属性和allowTaskReparenting属性配合使用时,
它允许allowTaskReparenting属性为true的Activity重新回到名字和TaskAffinity相同的任务栈中。
比如,应用1有一个Activity(非主Activity),为ActivityA(standard模式,TaskAffinity属性为应用1包名),应用2有一个Activity(非主Activity),为ActivityX(standard模式,TaskAffinity属性为应用2包名,allowTaskReparenting属性为true)。首先ActivityA启动ActivityX,系统会创建ActivityX实例并放到ActivityA实例所在的任务栈中;然后按下home键,在launch界面启动应用2,这时显示在前台的是ActivityX而不是主Activity,即ActivityX从名字为应用1包名的任务栈中转移到了名字为应用2包名的任务栈中。
二、Activity的Flags
FLAG_ACTIVITY_NEW_TASK
这个标记位的作用是为Activity指定"singleTask"启动模式。
FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定"singleTop"启动模式。
FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈,至于Activity自身是否出栈跟其启动模式有关系。
比如,同一应用中,ActivityA启动了ActivityB(ActivityB是standard模式),那么ActivityB就会进入到ActivityA所在的任务栈中,这时ActivityB启动ActivityA,并设置标记位FLAG_ACTIVITY_CLEAR_TOP,分以下两个情况:
情况1. 如果ActivityA是singleTask模式,
ActivityB会出栈,ActivityA会回调onNewIntent方法(Activity的onNewIntent、onRestart、onStart、onResume方法会被系统调用)。
情况2. 如果ActivityA是standard模式,
ActivityB跟ActivityA都会出栈,系统会重新创建ActivityA实例并放入栈中(Activity的onCreate、onStart、onResume方法会被系统调用)。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS(测试无效果)
具有这个标记的Activity不会出现在历史Activity的列表中,等同于android:excludeFromRecents="true"。
三、指定启动模式
方式1:通过AndroidManifest指定
<activity android:name=".ActivityA"
android:launchMode="singleTask"/>
方式2:通过Intent设置标志位指定
Intent intent = new Intent(MainActivity.this, ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
两种方式的区别:
以上两种方式都可以为Activity指定启动模式,但是在优先级上,方式2的优先级要高于方式1,当两种方式同时存在时,以方式2为准。
四、查看任务栈
//命令
adb shell dumpsys activity
//log
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Running activities (most recent first):
TaskRecord{33bfcd5 #2209 A=com.tomorrow.androidtest8 U=0 sz=1}
Run #3: ActivityRecord{23fb6ed u0 com.tomorrow.androidtest8/.ActivityY t2209}
TaskRecord{951bcea #2208 A=com.tomorrow.androidtest7 U=0 sz=3}
Run #2: ActivityRecord{237f43b u0 com.tomorrow.androidtest8/.ActivityX t2208}
Run #1: ActivityRecord{6e79040 u0 com.tomorrow.androidtest7/.ActivityA t2208}
Run #0: ActivityRecord{5354310 u0 com.tomorrow.androidtest7/.MainActivity t2208}