Activity 的四种启动模式(launchMode)
一. launchMode的类型
有四种:
- standard
- singleTop
- singleTask
- singleInstance
二. 如何设置?
大多数情况下, Activity的启动模式是在AndroidManifest.xml 中直接配置的,具体方法就是找到相应的activity,然后添加 ````android:launchMode```标签即可:
<activity
android:name=".MainActivity"
android:label="singleTask launchMode"
android:launchMode="singleTask">
三. 任务栈
任务栈,其实就用来放置和管理 Activity 实例的容器,之所以叫栈是因为里面的 Activity 是以栈的形式进行放置的,遵循先进后出的原则;
Android 在点击桌面启动 App 的时候,系统会为它创建一个Task,用来放置根 Activity, 此时一般情况下通过根 Activity 启动的其他 Activity 都会放在这个 Task 中,即新启动的Activity会被压入启动它的那个Activity的栈中,并将新启动的 Activity放置在栈的顶端显示它; 当用户按下回退键时,就会按从栈顶到栈底部的顺序依次将Activity 在栈中弹出.
总结一下:
- 任务栈具有后进先出的特性,用于存放 Activity 组件;
- 启动一个 App 的时候,系统会为它默认创建 Task,用来放置 Activity;
- 默认启动 Activity 会放在同一个 Task 中,新启动的 Activity 会被压入启动它的那个 Activity 的栈中,并且显示它。当用户连续按下 Back 键时,该 Task 里的 Activity 就会被依次弹出;
- 一个 App 中可能不止一个任务栈 Task,某些特殊情况下,单独一个 Actvity 可以独享一个任务栈;
- 一个 任务栈 Task 中的 Activity 可以来自不同的 App,同一个 App 的 Activity 也可能不在一个 Task中;
通过adb命令去查看 Activity 栈的状态
adb shell dumpsys activity
//查看当前前台应用的任务栈信息
adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
20170622234251793.png
四. launchMode的类型
1. standard
这是 Activity 是默认的启动模式, 如果我们在 AndroidManifest.xml
中不明确指出该 Activity 的启动模式,则会以 standard 的方式启动, 即可以不用写配置。
这种模式下,当我们发送一个 intent 启动该 Activity 后,该 Activity 总是被创建一个新的实例来工作。 因此,在这种模式下,同一个任务栈中可以有多个相同的实例,也允许多个相同Activity叠加.
比如有一个写邮件的场景,如果同时发送5个 standard 模式的 intent 来启动该写邮件的 Activity,那么将会启动5个不同的 Activity,出现5个写邮件的界面,这显然是不合适的,正确的方式应该是不管我们启动几次,应该总是有一个写邮件的界面。
开发过程中会遇到两种情况:
这种启动模式适合绝大多数Activity
1. 本应用内打开
新创建的 Activity 实例放入本应用,即发送 intent 的 task 栈的顶部
2. 不同应用间打开
跨应用打开一个activity,的时候会在 系统的 Recent app 页面显示两个独立项,但是此时它们两个 Activity 仍然是在一个栈中。
3. 跨进程调用启动
标准启动模式的 Activity 被跨进程调用,在 Android 5.0之前新启动的Activity 实例会放入发送 Intent 的 Task 的栈的顶部,尽管它们属于不同的程序; 在 Android 5.0之后,新启动的 Activity就会放入刚创建的 Task中。
2. singleTop
栈顶复用模式,如果要启动 Activity 存在于任务栈中,并且位于任务栈的顶部,就不会创建新的 Activity 实例,而是会调用 该 Activity 的 onNewIntent() 方法, 避免栈顶的 Activity 被重复的创建。
使用 singleTop 的 Activity 也可以创建很多个实例在同一个任务栈Task中, 与 standard 模式不同的是:
-
如果被调用的目标 Activity 已经位于调用者 Activity 所在栈的栈顶,则不创建新实例,而是使用当前的 Activity 实例,并调用这个 Activity 实例的 onNewIntent() 方法;
-
如果被调用的 Activity 的实例已经存在但不位于栈顶,那这个被调用的 Activity 依然会被创建。
跨进程调用启动
同standard模式,如果是外部程序启动 singleTop 的 Activity,在 Android 5.0之前新创建的 Activity会位于调用者的任务栈Task 中,Android 5.0及以后会放入新的Task中。
这种模式多用在通过通知栏或者通知消息启动 Activity 的时候, 如果用户已经在当前 Activity,此时用户如果点击了一条推送消息后或者从通知栏点击进去也需要跳转到当前 Activity,那么为了避免 Activity 的重复打开,则需要将该 Activity 设置为 singleTop 并且复写 onNewIntent() ;
还有一种情况就是当我们连续点击一个按钮的时候有会启动好几个相同的 Activity 的时候,这时候可以将 该 Activity 设置为 singleTop ,连续点击的时候就只能启动一个 Activity;其实当这个 Activity 启动比较慢的时候还是会出现连续点击启动好几个 Activity 的问题,因为当该 Activity 一个实例都还没有的时候连续点击依然会启动多个, 如果遇到这种情况建议使用标志位去解决。
3. singleTask
栈内复用模式, Activity 只会在任务栈里面存在一个实例。如果要启动的 Activity 在任务栈 Task 中已经存在,就不会创建新的 Activity 实例,而是复用这个已经存在的 Activity,调用 onNewIntent() 方法,并且清空这个 Activity 任务栈之上所有的其他 Activity。
应用场景:
App的主页, 对于大部分应用,当我们在主界面点击回退按钮的时候都是退出应用,那么当我们第一次进入主界面之后,主界面位于栈底,以后不管我们打开了多少个Activity,只要我们再次回到主界面,都应该使用将主界面Activity上所有的Activity移除的方式来让主界面Activity处于栈顶,而不是往栈顶新加一个主界面Activity的实例,通过这种方式能够保证退出应用时所有的Activity都能报销毁。
开发过程中会遇到三种情况:
1. 被启动的 Activity 在 Task 栈中不存在:
当被启动的 Activity 的启动模式为 singleTask时,如果该 Activity 在 Task 栈中不存在,则会创建一个新的实例放在栈顶并启动;
2. 被启动的 Activity 在 Task 栈中存在:
如果该 Activity 在 Task 栈中存在,则会将栈中该 Activity 之上的其他 Activity 全部清空,并且调用该 Activity 的 onNewIntent() 方法;
4. singleInstance
单一实例模式,与 singleTask 很相似, 具有 singleTask 的全部特性之外,又比singleTask 多了一种特性:
设置了该模式的 Activity 只能位于一个单独的任务栈中,不能在有其他 Activity , 其他任何从该 Activity 启动的其他 Activity 都会放到其他的任务栈中。
singleInstance 模式的 Activity 整个手机操作系统里面只有一个实例存在。不同的应用去打开这个 Activity 共享公用的同一个 Activity。他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。
应用场景:
呼叫来电界面。这种模式的使用情况比较罕见,在Launcher中可能使用。或者你确定你需要使Activity只有一个实例。
LaunchMode Flag
Activity 的启动模式除了在 AndroidManifest 文件中设置以外,还可以通过 Intent的 Flag 来设置:
1. FLAG_ACTIVITY_NEW_TASK
使用一个新的任务栈 Task 来启动一个 Activity,但启动的每个Activity都将在一个新的任务栈 Task 中。该 Flag 通常使用在从Service 中启动 Activity 的场景,由于Service中并不存在Activity 任务栈,所以使用该 Flag 来创建一个新的Activity栈,并创建新的 Activity 实例。
2. FLAG_ACTIVITY_SINGLE_TOP
使用 singletop 模式启动一个Activity, 当这个Activity位于历史stack的顶端运行时,不再创建一个新的Activity实例;
3. FLAG_ACTIVITY_CLEAR_TOP
销毁目标Activity和它之上的所有Activity,重新创建目标Activity
4. FLAG_ACTIVITY_NO_HISTORY
一个 Activity 使用这种模式启动后,当该 Activity 启动其他Activity后,该 Activity 自己就消失了,不会保留在Activity栈中。
5. FLAG_ACTIVITY_BROUGHT_TO_FRONT
在launchMode中设置singleTask模式时系统会设定。
6. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
如果设置,新的Activity不会在最近启动的Activity的列表中保存。
7. FLAG_ACTIVITY_FORWARD_RESULT
如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的 Activity。
8. FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
这个标志一般不由应用程序代码设置,如果这个Activity是从历史记录里启动的(常按HOME键),系统会设定。