1.首先要了解任务和任务栈的概念
1.1. android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件。
1.2. 我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件,因此一个任务栈包含了一个activity的集合, android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。
1.3. 在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。
1.4. 需要注意的是,一个App中可能不止一个任务栈,某些特殊情况下,单独一个Actvity可以独享一个任务栈。还有一点就是一个Task中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个Task中。
更加详细的简介参考:https://developer.android.google.cn/guide/components/tasks-and-back-stack.html
2.LaunchMode基于自己的理解
2.1首先要知道launchMode有四种加载模式,分别是:
“standard”
“singleTop”
“singleTask”
“singleInstance”
默认模式是“standard”。
那么下面分别介绍每个模式的用途:
2.2standard-标准模式,也是默认的模式
处理的模式如图:
每次不管任务栈中是否存在某个Activity(例如:AActivity),每次都是在同一个任务栈起启动新的Activity,可以看一下测试的日志如下:
04-18 18:47:59.333 665-665/? I/AActivity onCreate: 131
04-18 18:48:05.572 665-665/com.dress.gold.interconnected.check I/BActivity onCreate: 131
04-18 18:48:07.288 665-665/com.dress.gold.interconnected.check I/CActivity onCreate: 131
04-18 18:48:08.153 665-665/com.dress.gold.interconnected.check I/AActivity onCreate: 131
04-18 18:48:13.086 665-665/com.dress.gold.interconnected.check I/BActivity onCreate: 131
04-18 18:48:14.133 665-665/com.dress.gold.interconnected.check I/CActivity onCreate: 131
04-18 18:48:15.317 665-665/com.dress.gold.interconnected.check I/AActivity onCreate: 131
输入日志的方法:
Log.i("CActivity onCreate","" + getTaskId());
getTaskId()方法获取当前任务栈的任务ID,可以帮助我们判断多个Activity是否在同一个任务栈中;
清单文件配置,为每个Activity配置standard模式,也可以不配置使用默认模式:
<activity android:name=".ui.testui.AActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" android:launchMode="standard"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ui.testui.BActivity" android:launchMode="standard"/> <activity android:name=".ui.testui.CActivity" android:launchMode="standard"/>
2.2singleTop-顶部模式
处理的模式如图:
若任务栈顶部存在该Activity,则不会重新创建该Activity(例如:CActivity),在onNewIntent方法中接收Intent,可以看一下测试的日志如下:
04-19 22:34:32.581 8803-8803/? I/AActivity onCreate: getTaskId()----137
04-19 22:34:35.874 8803-8803/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----137
04-19 22:34:37.416 8803-8803/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----137
04-19 22:34:38.928 8803-8803/com.dress.gold.interconnected.check I/CActivity onNewIntent: getTaskId()----137
04-19 22:34:41.011 8803-8803/com.dress.gold.interconnected.check I/CActivity onNewIntent: getTaskId()----137
04-19 22:34:41.645 8803-8803/com.dress.gold.interconnected.check I/CActivity onNewIntent: getTaskId()----137
04-19 22:34:42.325 8803-8803/com.dress.gold.interconnected.check I/CActivity onNewIntent: getTaskId()----137
若任务栈顶部不存在该Activity,则不会重新创建该Activity(例如:AActivity),可以看一下测试的日志如下:
04-19 22:35:55.513 9447-9447/? I/AActivity onCreate: getTaskId()----138
04-19 22:36:09.617 9447-9447/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----138
04-19 22:36:10.752 9447-9447/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----138
04-19 22:36:11.746 9447-9447/com.dress.gold.interconnected.check I/AActivity onCreate: getTaskId()----138
04-19 22:36:12.845 9447-9447/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----138
04-19 22:36:13.715 9447-9447/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----138
04-19 22:36:14.264 9447-9447/com.dress.gold.interconnected.check I/AActivity onCreate: getTaskId()----138
04-19 22:36:14.498 9447-9447/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----138
04-19 22:36:14.964 9447-9447/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----138
04-19 22:36:15.465 9447-9447/com.dress.gold.interconnected.check I/AActivity onCreate: getTaskId()----138
04-19 22:36:17.181 9447-9447/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----138
04-19 22:36:20.378 9447-9447/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----138
04-19 22:36:22.883 9447-9447/com.dress.gold.interconnected.check I/AActivity onCreate: getTaskId()----138
输入日志的方法:
Log.i("CActivity onCreate","" + getTaskId());
getTaskId()方法获取当前任务栈的任务ID,可以帮助我们判断多个Activity是否在同一个任务栈中;
清单文件配置,为每个Activity配置standard模式,也可以不配置使用默认模式:
<activity android:name=".ui.testui.AActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" android:launchMode="singleTop"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ui.testui.BActivity" android:launchMode="singleTop"/> <activity android:name=".ui.testui.CActivity" android:launchMode="singleTop"/>
2.3singleTask-单任务模式
处理的模式如图:
通过测试日志我们就可以分析出虽然我们把Activity都设置singleTask,通过任务ID发现并不是每次都去新建一个任务,
只是在原任务栈上添加Activity,在CActivity启动AActivity时只是把任务栈上的B和C销毁,让A显示在栈顶,A若已经存在则不回重新创建,onNewIntent接收传过来的Intent,
另一种情况则是若C去启动(singleTask模式)的D则会创建D,将D显示在栈顶,A->B->C->D;
总结,有则清除Activity顶部其他的Activity,恢复显示在栈顶,没有则创建新的Activity放在栈顶
可以看一下测试的日志如下:
04-19 22:45:45.668 14050-14050/? I/AActivity onCreate: getTaskId()----143
04-19 22:45:47.600 14050-14050/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----143
04-19 22:45:48.715 14050-14050/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----143
04-19 22:45:50.199 14050-14050/com.dress.gold.interconnected.check I/BActivity onDestroy: getTaskId()----143
04-19 22:45:50.208 14050-14050/com.dress.gold.interconnected.check I/AActivity onNewIntent: getTaskId()----143
04-19 22:45:50.547 14050-14050/com.dress.gold.interconnected.check I/CActivity onDestroy: getTaskId()----143
上面的启动顺序A->B->C->A
04-19 22:46:07.017 14050-14050/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----14304-19 22:46:11.096 14050-14050/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----143
04-19 22:46:12.398 14050-14050/com.dress.gold.interconnected.check I/BActivity onDestroy: getTaskId()----143
04-19 22:46:12.407 14050-14050/com.dress.gold.interconnected.check I/AActivity onNewIntent: getTaskId()----143
04-19 22:46:12.729 14050-14050/com.dress.gold.interconnected.check I/CActivity onDestroy: getTaskId()----143
上面的启动顺序A->B->C->A
输入日志的方法:
Log.i("CActivity onCreate","" + getTaskId());
getTaskId()方法获取当前任务栈的任务ID,可以帮助我们判断多个Activity是否在同一个任务栈中;
清单文件配置,为每个Activity配置standard模式,也可以不配置使用默认模式:
<activity android:name=".ui.testui.AActivity" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ui.testui.BActivity" android:launchMode="singleTask"/> <activity android:name=".ui.testui.CActivity" android:launchMode="singleTask"/>
2.4singleInstance-单例模式
即指每次都是创建一个新的Activity在新的任务中
处理的模式如图:
通过下面的日志可以发现若启动BActivity的实例没有存在则新开一个任务栈,若存在则不会重新创建Activity,通过onNewIntent方法接收Intent;
总结:就是singleInstance模式的Activity任务栈中只能有一个Activity;
注意事项,若A启动B,在B页面回去后台以后,再回到前台时,在B页面返回按钮则直接回到主屏幕界面,无法回到A;
可以看一下测试的日志如下:
04-19 23:05:06.015 21507-21507/? I/AActivity onCreate: getTaskId()----144
04-19 23:05:20.310 21507-21507/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----14504-19 23:05:21.955 21507-21507/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----146
04-19 23:05:23.445 21507-21507/com.dress.gold.interconnected.check I/AActivity onNewIntent: getTaskId()----144
04-19 23:05:25.763 21507-21507/com.dress.gold.interconnected.check I/BActivity onNewIntent: getTaskId()----145
04-19 23:05:27.613 21507-21507/com.dress.gold.interconnected.check I/CActivity onNewIntent: getTaskId()----146
04-19 23:05:32.313 21507-21507/com.dress.gold.interconnected.check I/AActivity onNewIntent: getTaskId()----144
上面的启动顺序A->B->C->A
输入日志的方法:
Log.i("CActivity onCreate","" + getTaskId());
getTaskId()方法获取当前任务栈的任务ID,可以帮助我们判断多个Activity是否在同一个任务栈中;
清单文件配置,为每个Activity配置standard模式,也可以不配置使用默认模式:
<activity android:name=".ui.testui.AActivity" android:launchMode="singleInstance"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ui.testui.BActivity" android:launchMode="singleInstance"/> <activity android:name=".ui.testui.CActivity" android:launchMode="singleInstance"/>
3.launchMode的官方解释
android:launchMode有关应如何启动 Activity 的指令。共有四种模式与 Intent 对象中的 Activity 标志(FLAG_ACTIVITY_* 常量)协同工作,以确定在调用 Activity 处理 Intent 时应执行的操作。 这些模式是:
“standard”
“singleTop”
“singleTask”
“singleInstance”
默认模式是“standard”。
如下表所示,这些模式分为两大类,“standard”和“singleTop”Activity 为一类,“singleTask”和“singleInstance”为另一类。使用“standard”或“singleTop”启动模式的 Activity 可多次实例化。 实例可归属任何任务,并且可以位于 Activity 堆栈中的任何位置。 它们通常启动到名为 的任务之中(除非 Intent 对象包含 startActivity() 指令,在此情况下会选择其他任务 — 请参阅 taskAffinity 属性)。FLAG_ACTIVITY_NEW_TASK
相比之下,“singleTask”和“singleInstance”Activity 只能启动任务。 它们始终位于 Activity 堆栈的根位置。此外,设备一次只能保留一个 Activity 实例 — 只允许一个此类任务。
“standard”和“singleTop”模式只在一个方面有差异: 每次“standard”Activity 有新的 Intent 时,系统都会创建新的类实例来响应该 Intent。每个实例处理单个 Intent。同理,也可创建新的“singleTop”Activity 实例来处理新的 Intent。 不过,如果目标任务在其堆栈顶部已有一个 Activity 实例,那么该实例将接收新 Intent(通过调用 onNewIntent());此时不会创建新实例。在其他情况下 — 例如,如果“singleTop”的一个现有实例虽在目标任务内,但未处于堆栈顶部,或者虽然位于堆栈顶部,但不在目标任务中 — 则系统会创建一个新实例并将其推送到堆栈上。
同理,如果您向上导航到当前堆栈上的某个 Activity,该行为由父 Activity 的启动模式决定。 如果父 Activity 有启动模式 singleTop(或 upIntent 包含 FLAG_ACTIVITY_CLEAR_TOP),则系统会将该父项置于堆栈顶部,并保留其状态。 导航 Intent 由父 Activity 的 onNewIntent() 方法接收。 如果父 Activity 有启动模式 standard(并且 up Intent 不包含 FLAG_ACTIVITY_CLEAR_TOP),则系统会将当前 Activity 及其父项同时弹出堆栈,并创建一个新的父 Activity 实例来接收导航 Intent。
“singleTask”和“singleInstance”模式同样只在一个方面有差异: “singleTask”Activity 允许其他 Activity 成为其任务的组成部分。 它始终位于其任务的根位置,但其他 Activity(必然是“standard”和“singleTop”Activity)可以启动到该任务中。 相反,“singleInstance”Activity 则不允许其他 Activity 成为其任务的组成部分。它是任务中唯一的 Activity。 如果它启动另一个 Activity,系统会将该 Activity 分配给其他任务 — 就好像 Intent 中包含 FLAG_ACTIVITY_NEW_TASK 一样。
| 用例 | 启动模式 | 多个实例? | 注释 |
|---|---|---|---|
| 大多数 Activity 的正常启动 | “standard” |
是 | 默认值。系统始终会在目标任务中创建新的 Activity 实例并向其传送 Intent。 |
“singleTop” |
有条件 | 如果目标任务的顶部已存在一个 Activity 实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建新的 Activity 实例。 |
|
| 专用启动 (不建议用作常规用途) |
“singleTask” |
否 | 系统在新任务的根位置创建 Activity 并向其传送 Intent。 不过,如果已存在一个 Activity 实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建新的 Activity 实例。 |
“singleInstance” |
否 | 与“singleTask"”相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。 该 Activity 始终是其任务唯一仅有的成员。 |
如上表所示,standard 是默认模式,并且适用于大多数的 Activity 类型。对许多类型的 Activity 而言,SingleTop 也是一个常见并且有用的启动模式。 其他模式 — singleTask 和 singleInstance - 不适合 大多数应用因为它们所形成的交互模式可能让用户感到陌生,并且与大多数其他应用迥异。
无论您选择哪一种启动模式,请务必在启动期间以及使用返回按钮从其他 Activity 和任务返回该 Activity 时对其进行易用性测试。
如需了解有关启动模式及其与 Intent 标志交互的详细信息,请参阅任务和返回栈文档。
4.其他
4.1Intent中Flag使用
4.1.1事实上在代码中我们并不是一定在AndroidManifest.xml中配置启动模式,也可以通过调用startActivity()方法中设置Flag的方式到达相同的效果;
例如下面的默认配置:
<activity android:name=".ui.testui.AActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ui.testui.BActivity"/> <activity android:name=".ui.testui.CActivity"/>
A->B->C,可是我们直接回到A页面,而不是创新创建A界面,那么Intent中的Flag就可以实现这种效果,实现代码:
Intent intent = new Intent(this, AActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
//Intent.FLAG_ACTIVITY_CLEAR_TOP清除AActivity顶部的Activity,Intent.FLAG_ACTIVITY_SINGLE_TOP等于singleTop模式,顶部存在则不创建,在onNewIntent方法中接收Intent startActivity(intent);
输出日志如下:
04-19 23:34:21.187 915-915/com.dress.gold.interconnected.check I/AActivity onCreate: getTaskId()----148
04-19 23:34:23.218 915-915/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----148
04-19 23:34:24.052 915-915/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----148
04-19 23:34:26.202 915-915/com.dress.gold.interconnected.check I/BActivity onDestroy: getTaskId()----148
04-19 23:34:26.218 915-915/com.dress.gold.interconnected.check I/AActivity onNewIntent: getTaskId()----148
04-19 23:34:26.544 915-915/com.dress.gold.interconnected.check I/CActivity onDestroy: getTaskId()----148
4.1.2还有其他的Flag,例如FLAG_ACTIVITY_NEW_TASK等同于singleTask,当前也可以和FLAG_ACTIVITY_CLEAR_TOP一起使用,但是效果和上面稍有区别,在C启动A使用会把A以上的Activity都销毁掉,同时A也会销毁掉,在A的位置新建一个A;
可以看一下输出日志就明白了:
04-19 23:41:39.462 4461-4461/? I/AActivity onCreate: getTaskId()----149
04-19 23:41:40.918 4461-4461/com.dress.gold.interconnected.check I/BActivity onCreate: getTaskId()----149
04-19 23:41:41.937 4461-4461/com.dress.gold.interconnected.check I/CActivity onCreate: getTaskId()----149
04-19 23:41:43.192 4461-4461/com.dress.gold.interconnected.check I/BActivity onDestroy: getTaskId()----149
04-19 23:41:43.199 4461-4461/com.dress.gold.interconnected.check I/AActivity onDestroy: getTaskId()----149
04-19 23:41:43.212 4461-4461/com.dress.gold.interconnected.check I/AActivity onCreate: getTaskId()----149
04-19 23:41:43.676 4461-4461/com.dress.gold.interconnected.check I/CActivity onDestroy: getTaskId()----149
官方解释:
FLAG_ACTIVITY_CLEAR_TOP如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。产生这种行为的 launchMode 属性没有值。
FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK 结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。
注:如果指定 Activity 的启动模式为 "standard",则该 Activity 也会从堆栈中移除,并在其位置启动一个新实例,以便处理传入的 Intent。 这是因为当启动模式为 "standard" 时,将始终为新 Intent 创建新实例。
4.2应用和应用之前交互说明
若X应用启动Y应用,即使Y应用中的AActivity已经启动,若AActivity启动模式设置standard、singleTop或者singleTask模式,则不管Y中是否启动了AActvity,都会在X应用的任务栈添加一个新的AActivity;
参考:https://developer.android.google.cn/guide/components/tasks-and-back-stack.html