Android四种启动方式
Activity有四种启动方式:
以下是Android developer介绍的,但是实际情况会有所出入
| 使用场景 | Launch Mode | 是否多实例 | 介绍 |
| —————- | ————– | ———- | ——————————————————————————————————————————————————————————————————————— |
| 绝大多数使用场景 | standard | 是 | 默认模式,系统总会在目标任务栈中创建一个新实例,并把intent传给它 |
| 绝大多数使用场景 | singleTop | 根据情况 | 如果一个实例存在且位于目标任务栈的顶端,那么系统会将intent通过onNewIntent传给该activity,而不会创建一个新实例 |
| 特殊场景使用 | singleTask | 否 | 系统创建一个新的任务栈和一个新的activity实例,将activity实例放在栈底。但是,如果已经有一个这个Activity的实例,系统会回调这个实例的onNewIntent方法,而不会创建新实例(注意:此处介绍与实际情况有出入,后面会详细介绍) |
| 特殊场景使用 | singleInstance | 否 | 和singleTask几乎一样,不同的是,singleInstance存在的任务栈,不会有其他任何activity,该Activity是其所在任务栈内唯一的一个Activity |
standard和singleTop
这两个模式看似简单,其实仔细思考还是有一些需要注意的点的。
我们从最简单的开始测试,现有如下两个Activity1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar" />
测试流程MainActivity->Main2Activity->Main2Activity,打印当前任务栈,发现只有两个Activity:栈底MainActivity,栈顶Main2Activity,并且,在第二次启动Main2Activity时,没有创建新实例,而是回调了Main2Activity的onNewIntent方法。
对于standard和singleTop,如无特别操作,新建的实例都会压入默认任务栈,上面两次启动Activity都是在默认的任务栈中。那么对于这种类型的Activity,我们可以让他们启动在另一个任务中么?答案是肯定的。
我们在启动Main2Activity时,Intent可以设置Flag:1
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
根据官方文档,当设置了FLAG_ACTIVITY_NEW_TASK之后,会使用一个新的任务来承接要启动的Activity,但是实际情况是这样么?还是MainActivity->Main2Activity->Main2Activity的启动顺序,打印任务栈,发现还是只有一个任务栈,Main2Activity表现的和没有加Flag时一模一样。而出现这种情况的原因就是我们没有设置Main2Activity的taskAffinity。
taskAffinity: 每个Activity都有一个taskAffinity,表示这个Activity在启动时如果需要选择任务栈,那么选择的依据就是taskAffinity,为什么说如果需要选择呢?这是因为standard和singleTop模式下,即便设置了taskAffinity参数,Activity在启动后会直接进入到启动它的Activity所在的栈,而不会考虑根据taskAffinity去选择,除非在启动时给intent添加FLAG_ACTIVITY_NEW_TASK FLAG,这相当于给了Activity选择任务栈的权利,taskAffinity也会生效。而singleTask和singleInstance在整个系统中是单例,所以天生在启动时需要遍历所有任务栈,判断当前是否存在实例,如果有实例,则将已存在实例的上方Activity弹出,回调onNewIntent。
将上面manifest修改如下,给Main2Activity添加taskAffinity1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2"
android:launchMode="singleTop"
android:taskAffinity="com.test"
android:theme="@style/AppTheme.NoActionBar" />
测试流程MainActivity->Main2Activity->Main2Activity,打印所有任务栈,这里给出打印任务栈代码,打印出每个任务栈的Activity数量、affiliatedTaskId、栈底Activity、栈顶Activity。1
2
3
4
5
6
7
8
9
10
11public static void printActivities(AppCompatActivity activity){
ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
for (int i = 0; i < appTasks.size(); i++) {
Util.print("numActivities: " + appTasks.get(i).getTaskInfo().numActivities + "\n");
Util.print("affiliatedTaskId: " + appTasks.get(i).getTaskInfo().affiliatedTaskId + "\n");
Util.print("baseActivity: " + appTasks.get(i).getTaskInfo().baseActivity + "\n");
Util.print("topActivity: " + appTasks.get(i).getTaskInfo().topActivity + "\n");
}
Util.print("================================>>>>>>>\n");
}
结果如下1
2
3
4
5
6
7
8
9
10
11
1204-29 00:39:30.563 8113-8113/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 00:39:30.567 8113-8113/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 287 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
04-29 00:39:33.104 8113-8113/study.zgq.com.androidstudy D/nanwei: total tasks: 2
04-29 00:39:33.108 8113-8113/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 288 baseActivity: Main2Activity topActivity: Main2Activity
04-29 00:39:33.110 8113-8113/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 287 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
04-29 00:39:34.099 8113-8113/study.zgq.com.androidstudy D/nanwei: +++++++++++++>>>>new intent
04-29 00:39:35.653 8113-8113/study.zgq.com.androidstudy D/nanwei: total tasks: 2
04-29 00:39:35.656 8113-8113/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 288 baseActivity: Main2Activity topActivity: Main2Activity
04-29 00:39:35.661 8113-8113/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 287 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
可以看出,第一次启动Main2Activity后有两个任务栈,Main2Activity于一个新的任务栈中,且处于前台。再次启动Main2Activity,发现两个任务栈状态均未改变,但是回调了Main2Activity的onNewIntent。
那么,我们在第二个栈中重新启动MainActivity呢?也就是MainActivity->Main2Activity->Main2Activity->MainActivity的启动顺序,这里是结果1
2
3
4
5
6
7
8
9
10
1104-29 00:57:33.090 8522-8522/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 00:57:33.093 8522-8522/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 290 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
04-29 00:57:35.830 8522-8522/study.zgq.com.androidstudy D/nanwei: total tasks: 2
04-29 00:57:35.832 8522-8522/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 291 baseActivity: Main2Activity topActivity: Main2Activity
04-29 00:57:35.835 8522-8522/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 290 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
04-29 00:57:38.594 8522-8522/study.zgq.com.androidstudy D/nanwei: total tasks: 2
04-29 00:57:38.597 8522-8522/study.zgq.com.androidstudy D/nanwei: numActivities: 2 affiliatedTaskId: 291 baseActivity: Main2Activity topActivity: MainActivity
04-29 00:57:38.602 8522-8522/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 290 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
可以看出来,在第二个任务栈中新创建了一个MainActivity。那么,我们可以将新创建的MainActivity压入第一个任务栈么?答案也是可以的:先修改manifest,给MainActivity指定taskAffinity:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:taskAffinity="com.test.main"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2"
android:launchMode="singleTop"
android:taskAffinity="com.test"
android:theme="@style/AppTheme.NoActionBar" />
同时在从Main2Activity启动MainActivity时添加1
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
还是按照MainActivity->Main2Activity->Main2Activity->MainActivity的启动顺序,看结果1
2
3
4
5
6
7
8
9
10
1104-29 01:08:54.490 8840-8840/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 01:08:54.493 8840-8840/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 292 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
04-29 01:08:57.141 8840-8840/study.zgq.com.androidstudy D/nanwei: total tasks: 2
04-29 01:08:57.145 8840-8840/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 293 baseActivity: Main2Activity topActivity: Main2Activity
04-29 01:08:57.148 8840-8840/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 292 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>
04-29 01:09:00.084 8840-8840/study.zgq.com.androidstudy D/nanwei: total tasks: 2
04-29 01:09:00.089 8840-8840/study.zgq.com.androidstudy D/nanwei: numActivities: 2 affiliatedTaskId: 292 baseActivity: MainActivity topActivity: MainActivity
04-29 01:09:00.090 8840-8840/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 293 baseActivity: Main2Activity topActivity: Main2Activity
================================>>>>>>>
可以看出来,在初始task内重新创建了一个MainActivity。
上面我们使用了一个Flag:FLAG_ACTIVITY_NEW_TASK,其实还有一个常见的Flag是FLAG_ACTIVITY_CLEAR_TOP,顾名思义,在启动Activity时,如果目标栈内已经有了实例,那么会弹出其上的所有Activity,然后回调onNewIntent方法(Activity launchMode为singleTop)或者销毁栈内的实例以及其上面的实例,重新创建新实例(Activity launchMode为standard)。举例如下:有MainActivity和Main2Activity,launchMode都是standard,启动顺序MainActivity->Main2Activity->MainActivity,其中MainActivity->Main2Activity是直接调用启动,Main2Activity->MainActivity时intent添加flag:FLAG_ACTIVITY_CLEAR_TOP。1
2
3
4
5
6
7
8
9
10
11
12
13
14
1504-29 22:49:51.307 19805-19805/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onCreate
04-29 22:49:51.687 19805-19805/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onResume
04-29 22:49:54.863 19805-19805/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 22:49:54.866 19805-19805/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 308 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>启动分隔
04-29 22:49:56.152 19805-19805/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onPause
04-29 22:49:56.680 19805-19805/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onStop
04-29 22:49:57.132 19805-19805/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 22:49:57.135 19805-19805/study.zgq.com.androidstudy D/nanwei: numActivities: 2 affiliatedTaskId: 308 baseActivity: MainActivity topActivity: Main2Activity
================================>>>>>>>启动分隔
04-29 22:49:58.135 19805-19805/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onCreate
04-29 22:49:58.167 19805-19805/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onResume
04-29 22:49:59.280 19805-19805/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 22:49:59.284 19805-19805/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 308 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>启动分隔
从上面可以看出来,standard模式的activity被带有FLAG_ACTIVITY_CLEAR_TOP的intent重新启动时,重新经历一次完整的生命周期,也就是说会弹出自身以及其上的所有activity,并重新创建一个新的实例,这也迎合了standard模式在任何情况下都会重新创建一个新实例。
接着上面的例子,如果MainActivity launchMode是singleTop,那么结果是1
2
3
4
5
6
7
8
9
10
11
12
13
14
1504-29 22:43:57.198 19471-19471/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onCreate
04-29 22:43:57.597 19471-19471/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onResume
04-29 22:44:00.439 19471-19471/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 22:44:00.444 19471-19471/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 307 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>启动分隔
04-29 22:44:01.589 19471-19471/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onPause
04-29 22:44:02.151 19471-19471/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onStop
04-29 22:44:02.692 19471-19471/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 22:44:02.694 19471-19471/study.zgq.com.androidstudy D/nanwei: numActivities: 2 affiliatedTaskId: 307 baseActivity: MainActivity topActivity: Main2Activity
================================>>>>>>>启动分隔
04-29 22:44:03.967 19471-19471/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onNewIntent
04-29 22:44:03.968 19471-19471/study.zgq.com.androidstudy D/nanwei: ------------>>MainActivity onResume
04-29 22:44:05.160 19471-19471/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 22:44:05.165 19471-19471/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 307 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>启动分隔
可见singleTop模式的activity在被带有FLAG_ACTIVITY_CLEAR_TOP的intent启动时,会弹出其上所有activity,并回调onNewIntent,而不会创建新实例。这也迎合了singleTop可以(注意,只是可以)维持一个实例的特性。需要注意的是,FLAG_ACTIVITY_CLEAR_TOP起作用的是离栈顶最近的Activity,什么意思呢?比如当前栈内从低向上为A-A-A-B,然后B通过FLAG_ACTIVITY_CLEAR_TOP启动A,那么结果是A-A-A。
singleTask和singleInstance
我们知道,假设有launchMode为singleTask的Activity1,如果任务栈中已经有了一个Activity1的实例,那么当重新要启动一个新的Activity1时,不会创建新的实例,而会将Activity1上的所有Activity全部弹出栈,然后回调onNewIntent。也就是说,同一个栈中,只能有一个Activity1的实例。那么,如果不同栈中呢?能同时各出现一个实例么?答案是否定的,为什么呢?因为如果要使同一个Activity的多个实例处于不同任务栈中,那么这个Activity势必要有多个不同的taskAffinity,而Android是不支持多个taskAffinity的,这也违反task的设计理念。所以singleTask的Activity在系统中唯一实例。
需要提醒的是,官方文档中对于singleTask的解释:”系统创建一个新的任务栈和一个新的activity实例,将activity实例放在栈底“,上面这句话其实是有待商榷的,因为他的实现是有限制的,如果我们仅仅将Activity的launchMode设置为singleTask,那么在启动时,并不会像上面所说,创建新的任务栈然后放入栈底,二是和standard模式一样,直接创建一个新的放入栈顶。如下例:MainActivity是standard模式,Main2Activity是singleTask模式,启动顺序MainActivity->Main2Activity。1
2
3
4
5
604-29 23:15:20.774 21777-21777/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 23:15:20.779 21777-21777/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 316 baseActivity: MainActivity topActivity: MainActivity
================================>>>>>>>启动分隔
04-29 23:15:23.071 21777-21777/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 23:15:23.075 21777-21777/study.zgq.com.androidstudy D/nanwei: numActivities: 2 affiliatedTaskId: 316 baseActivity: MainActivity topActivity: Main2Activity
================================>>>>>>>启动分隔
从结果可以看出,Main2Activity的表现和standard模式一致,那么怎么才能出现文档的情况呢?那就是设置Main2Activity的taskAffinity,可以说,taskAffinity是判断是否创建新任务栈的唯一考量标准。
而singleInstance和singleTask的特性基本一致,区别在于,singleInstance是唯一一个存在于任务栈的activity,不可能有其他的activity,比如有MainActivity(standard)和Main2Activity(singleInstance),MainActivity启动Main2Activity,结果是1
2
3
4
5
604-29 23:31:36.685 22677-22677/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 23:31:36.690 22677-22677/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 318 baseActivity: MainActivity topActivity: MainActivity
04-29 23:31:36.691 22677-22677/study.zgq.com.androidstudy D/nanwei: ================================>>>>>>>启动分隔
04-29 23:31:40.102 22677-22677/study.zgq.com.androidstudy D/nanwei: total tasks: 1
04-29 23:31:40.104 22677-22677/study.zgq.com.androidstudy D/nanwei: numActivities: 1 affiliatedTaskId: 319 baseActivity: Main2Activity topActivity: Main2Activity
================================>>>>>>>启动分隔
可以看出,taskAffinityId发生了变化,也即是说,启动Main2Activity后,新建了一个新的任务栈,然后把Main2Activity放入栈底。
总结
- standard模式任何时候都会重新创建实例,包括被FLAG_ACTIVITY_CLEAR_TOP启动时
- 对于能维护一个单例的模式,在使用FLAG_ACTIVITY_CLEAR_TOP时,都会回调onNewIntent,singleTop具有(注意,只是具有,并不是肯定是只有一个实例)维持同一个实例的特性,singleTask和singleInstance都是单例
- taskAffinity和singleInstance是考量是否创建新任务栈的唯二参考