Activity启动模式
声明启动模式有两种方式:
- 在清单文件中声明待启动的 Activity 的 launchMode 属性
- 代码中通过 Intent 启动 Activity 时,设置 flag
如果在一次启动过程中,两种方案都设置了,后者优先级比较高。清单文件的 launchMode 和 intent flag 都不能完全代替对方。
# 1. Task与Back-Stack
Task表示“作业”或“任务”,充当Activity的容器,Task使用Back-Stack保存管理各个Activity。在Android系统中,执行某个任务时可能存在多个与用户产生交互的Activity,Task则是这些Activity的容器。
(1) 执行Task的多个Activity不一定属于同一个应用程序
- 例如:A程序的Activity输入邮件内容,B程序的Activity来发送邮件
(2) 执行Task的多个Activity中,可能有同一个Activity的多个实例
- 例如:MainActivity也可以激活自身,而且可以反复操作,可能存在多个MainActivity的实例
# Back-Stack
Back-Stack表示“回退栈”,每个Back-Stack栈顶得Activity被置于前台,称之为Foreground Avtivity(前台Activity),遵循栈的规则
我们新打开的Activity,由于是由Task来管理的,都是存储在了Back-Stack之中;所以新的Activity是覆盖到了旧的Activity之上,显示的是当前处在栈顶的 Foreground-Activity
在Android中,系统用Task Stack(Back Stack)结构来存储管理启动的Activity对象
一个应用启动,系统就会为其创建一个对应的Task Stack来存储并管理该应用的Activity对象
只有最上面的任务栈的栈顶的Activity才能显示在窗口中
这也符合我们看到的界面结构,新出现的Activity总是覆盖在之前的Activity之上,按返回键则回退至上一个Activity
# 【小结】
- Task作为一个容器,使用Back-Stack保存、管理Activity
- 用户在使用Android时,可以启动多个Task
- 每个Task都是有聚合力的单元,它可以在用户启动一个新的Task或按下Home键时整体的被置于后台
- 无论Task是被置于前台还是后台,Back-Stack都是完整的保存。因此,用户在执行某个Task时,另外启动了新的Task,则原Task被置于后台;但当原Task重新置于前台时,它的Back Stack依然存在。
🚀就像我在用手机刷知乎,突然此时来了电话,那么被打开的知乎的ZHActivity就会被电话PhoneActivity覆盖。此时PhoneActivity位于前台栈顶,在Back-Stack中ZHActivity位于其下面。当我挂了电话之后,此时ZHActivity又回到了栈顶位置。
具体是回到知乎的首页、消息、我的的哪个Activity,要根据之前谁处在Back-Stack上面的位置,这都是被记录好了的
# 2. Activity的LaunchMode
LounchMode为启动模式,Activity的launchmode被配置为不同的值时,当尝试激活Activity时可能会受影响:
- 该Activity的实例数量不同
- 所在的Task不同(在Activity中通过
getTaskId
查看Task的ID) - 在Back-Stack中的列表不同
- 在Back-Stack中各Activity经历的生命周期不同
在AndroidManifest中配置:
# 2.0 Activity的4种launchMode
standard 标准模式
- 标准模式,每次激活Activity时均在当前任务栈中创建新的实例(每次均创建新的实例)
singleTop 栈顶时唯一
- 位于栈顶时唯一:如果当前Activity已位于当前任务栈栈顶,则不会创新的实例
singleTask 独立于任务栈
- 独立于任务栈,如果该Activity的实例不存在,创建并且获得栈顶位置;如果实例已存在,则将其上方的Activity均出栈,且该Activity获得栈顶位置
singleInstance 实例唯一
- 实例唯一,实例独占任务栈,且无论哪个任务栈共享同一个Activity实例
# 2.1 standard
标准模式,每次激活Activity时均在当前任务栈中创建新的实例(每次均创建新的实例)
# 2.2 singleTop
位于栈顶时唯一:如果当前Activity已位于当前任务栈栈顶,则不会创新的实例
- 当前Activity不存在 或者 已存在且位于栈顶:不会创建新的实例
- 待启动 Activity 已经位于源 Activity 所属的任务栈的栈顶时,不会创建新的 Activity,而是直接使用栈顶的 Activity,并回调它的 onNewIntent 方法,onCreate 和 onStart 不会被调用,直接回调 onResume
- 当前Activity存在且不位于栈顶:会创建新的实例位于栈顶
singleTop下,已位于栈顶则不会创建新的实例,否则均会创建新的Activity置于栈顶
# 2.3 singleTask
全局单实例,首先会寻找要启动的 Activity 想要的任务栈(默认或者 taskAffinity
属性指定),如果没有找到,则创建新的任务栈并将 Activity 实例放入。如果找到了想要的任务栈,这时候要判断栈中是否已经存在该 Activity 的实例,如果已经存在,会将该 Activity 以上的其他 Activity 实例弹出,把自己放到栈顶,同样也是回调 onNewIntent
和 onResume
。如果实例不存在,创建新的实例并压入栈中。
- 独立于任务栈,如果该Activity的实例不存在,创建并且获得栈顶位置;
- 如果实例已存在,则将其上方的Activity均出栈(顶出去),且该Activity获得栈顶位置
# taskAffinity
Affinity表示Activity的“亲属关系”。在AndroidManifest.xml文件中,可以为每个Activity配置taskAffinity属性,表示该Activity希望进入的Task,该属性的值是字符串。
默认情况下,同一个应用程序中各个Activity的taskAffinity属性值均相同,则表示使用Application的taskAffinity,如果Application没有指定该属性,则默认为项目的包名。
在配置taskAffinity属性时,取值应参照包名的命名格式,且不应该使用下划线_
,避免应用程序在编译打包时出现问题。
# singleTask与taskAffinity
一个应用程序在运行,但是却启动了两个任务
【情景1】
- SingleTaskActivity的launchMode配置为singleTask;
- SingleTaskActivity与激活它的Activity有相同的taskAffinity(使用默认,或显式的指定相同属性值)。
结论:SingleTaskActivity与激活它的Activity在相同的Task中。
【情景2】
- SingleTaskActivity的launchMode配置为singleTask;
- SingleTaskActivity与激活它的Activity配置了不同的taskAffinity。
结论:SingleTaskActivity与激活它的Activity在不同的Task中。
【情景3】
两个不相同的应用程序启动,但是却公用一个相同的Task
- SingleTaskActivity的launchMode配置为singleTask;使用另一个应用程序的Activity去激活SingleTaskActivity;
- SingleTaskActivity与激活它的Activity配置了相同的taskAffinity。
结论:SingleTaskActivity与另一个应用程序中激活它的Activity在相同的Task中。
# 2.4 singleInstance
全局单实例,首次启动时会创建新的 Activity 实例,并放入一个新的任务栈中,且 这个任务栈中只会有这一个实例。 后续启动不会再新建实例。
SingleInstance与taskAffinity
由被配置为singleInstance的Activity激活的其它Activity,会尝试放在存在“亲属”关系的Task中,如果没有匹配的Task存在,则会创建新的Task存放被激活的Activity。
# 3. 激活Activity与压栈
当Activity被激活时,它在Back-Stack中压栈,成为栈顶Activity,被显示且获得焦点
第一次启动某个应用程序
大多数Task一Home界面或应用程序列表为界面作为起点,当用户点击程序icon时,Task被创建且置于前台。系统检测到入口Activity后在Back-Stack中压栈。由于Task是新创建的,此时Task中只有一个Activity(入口Activity即时栈顶也是栈底),所以入口Activity获得焦点
# 3.1 保存Activity的状态
当同一个Task中,出现新的Activity压栈时 或者 当前Task被真题置于后台时,原Task栈顶的Activity被停止时的状态被保存
在程序的登录界面中,输入了用户信息,被来电中断,通话结束后,原登录界面中已经输入的信息依然存在
注意:由于系统可能因为资源不是而销毁一些Activity,所以并不能保证当Task被置于后台之后,再次回到前台时Activity的状态依然存在,因为很可能整个Activity都已经不存在了。
# 3.2 Activity的销毁与出栈
当用户点击"Back"键时,栈顶的Activity出栈,一旦出栈,则该Activity被终止,且资源被回收,即销毁。
# 3.3 Activity的状态与Back Stack
在Back Stack中,Activity的状态可能为:
- 当新激活的Activity压栈,则成为栈顶的Activity,该Activity处于运行态
- 当用户点击“Back”键,栈顶Activity出栈,且被销毁,处于终止态
- 在栈内,且处于栈顶的Activity,可能处于暂停(可见不可控)、停止态、终止态