本项目用于验证Android Activity与task的各种行为。 可以通过 Demo app 的界面操作结合 dumpsys activity 命令来观察。 此外,本项目也提供了一些 instrumentation test cases 用于自动验证。
在 Demo app 中,提供了如下列表中的各种Activity,覆盖到所有的 android:launchMode 和三种类型的 android:taskAffinity。
Activity name | android:launchMode | android:taskAffinity |
---|---|---|
MainActivity |
standard |
[default] |
Standard1Activity |
standard |
[default] |
Standard2Activity |
standard |
me.ycdev.task2 |
Standard3Activity |
standard |
[empty] |
SingleTop1Activity |
singleTop |
[default] |
SingleTop2Activity |
singleTop |
me.ycdev.task2 |
SingleTop3Activity |
singleTop |
[empty] |
SingleTask1Activity |
singleTask |
[default] |
SingleTask1Activity |
singleTask |
me.ycdev.task2 |
SingleTask1Activity |
singleTask |
me.ycdev.task2 |
SingleInstance1Activity |
singleInstance |
[default] |
SingleInstance2Activity |
singleInstance |
me.ycdev.task2 |
SingleInstance3Activity |
singleInstance |
me.ycdev.task2 |
此外,还有几个特殊的Activity:
-
allowTaskReparenting="true": SpecialReparentingActivity
-
android:finishOnTaskLaunch="true": SpecialFinishOnLaunchActivity
-
android:clearTaskOnLaunch="true": SpecialClearOnLaunchActivity
在各Activity界面,也可以指定各种 Intent flags 来启动新的Activity。
💡
|
可以使用如下命令来查看当前的 Activity 和 task 信息:
|
当 Activity A 启动 Activity B 时,需要回答如下问题:
-
Activity B在哪个task中运行?
-
Activity B是新建实例还是复用已有实例?
在不使用 Intent flags 的情况下,有如下情况:
Activity A’s android:launchMode | Activity B’s android:launchMode | Activity B 属于哪个task |
---|---|---|
standard, singleTop, singleTask |
standard, singleTop |
Activity B 的 android:taskAffinity 会被忽略,会在当前 Activity A 所在task中运行。 |
singleInstance |
standard, singleTop |
新建或者使用 Activity B 的 android:taskAffinity 指定的task。 |
[any] |
singleInstance |
Activity B 的 android:taskAffinity 会被忽略,系统为 Activity B单独维护一个唯一task。
|
[any] |
singleTask |
Activity B 会在它的 android:taskAffinity 指定的task中运行(可以是当前task)。
|
💡
|
由于 singleTask 和 singleInstance 运行在指定的task中,为了便于用户在任何时候 能够回到之前的工作,它们应该只用于 launcher Activity。 |
使用 Intent flags 启动Activity,情况较为复杂。
这个flag可以使 "standard" 和 "singleTop" 这两种 android:launchMode 的 android:taskAffinity 强制生效,用于指定task。
如果只使用 FLAG_ACTIVITY_NEW_TASK:
Activity A’s android:launchMode | Activity B’s android:launchMode | Activity B 属于哪个task |
---|---|---|
[any] |
standard,singleTop |
|
[any] |
singleInstance |
与不加flag的情况相同 |
[any] |
singleTask |
与不加flag的情况相同 |
用于清除目标task中的Activity B之上的所有Activity,同时:
-
如果Activity B的 android:launchMode 为 standard,且没有 FLAG_ACTIVITY_SINGLE_TOP, 那么 Activity B实例会被销毁,然后重新创建。
-
其它情况都是通过 onNewIntent 重用Activity B的实例。
当目标task中有多个Activity B时,选中哪个Activity B有如下规则:
-
如果Activity B的 android:launchMode 为 standard,且指定了 FLAG_ACTIVITY_NEW_TASK, 但没有指定 FLAG_ACTIVITY_SINGLE_TOP, 那么从栈顶向下的 第二个 Activity B 会被选中。
-
其它情况,从栈顶向下的第一个 Activity B 会被选中。
仅当目标Activity的 android:taskAffinity 指定的task存在时,这个flag才会起作用,将导致目标task被清空。 注意,目标task也可能就是当前task。
💡
|
官方文档要求跟 FLAG_ACTIVITY_NEW_TASK 一块使用。原因是只有跟 FLAG_ACTIVITY_NEW_TASK 一块使用时,FLAG_ACTIVITY_CLEAR_TASK 才能工作。但是,如果目标Activity是 singleTask 或者 singleInstance,那么可以不加 FLAG_ACTIVITY_NEW_TASK 。 |
这个属性可以让Activity实例在task之间转移,回到 android:taskAffinity 指定的task中。 默认值为false。
假如一个Activity A的launch mode为 standard 或者 singleTop, 它的 android:taskAffinity 为 "me.ycdev.task1",且它的 android:allowTaskReparenting 为 true。 当前有两个task在运行:"me.ycdev.task1" 和 "me.ycdev.task2", 且 Activity A的一个实例运行在当前task "me.ycdev.task2"中。 当用户切换回到 "me.ycdev.task1" 时,系统会把 "me.ycdev.task2" 中的Activity A实例转移到 "me.ycdev.task1"中。
💡
|
需要使用 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 切换回 "me.ycdev.task1", 才会触发这个特性。例如,launcher启动app时就会添加这个flag,所以能触发这个功能。 但通过recent键来切换task是无法触发这个功能的。 |
在某些特殊情况下,当用户从launcher重新打开一个app时,系统会清空已有task(如果存在的话)。 例如,当用户离开这个task超过了指定的时间(如30分钟)。 如果想你的app始终回到之前的task(即不允许系统清空task),那么可以把该属性设置为true(默认值为false)。 这是一个用户体验方面的设计,更改前跟PM好好讨论下。
该属性仅针对task的Root Activity有效,默认值false。
该属性与 android:alwaysRetainTaskState 相反。当task回到前台时, task中的Activity会被清空,仅留下Root Activity。
该属性仅针对task的Root Activity有效,默认值false。
当多个Activity被依次启动后,可能存在多个task。此时,如果一直按back键退出, 那么Activity的退出顺序是这样的(跟启动顺序可能不同),一个task一个task退出: task1[Activity1, Activity2,…],task2[ActivityA,ActivityB]…
如果按Home键退回到了桌面或者仅是按了一下Recent键,再回到先前的task, 那么此时的back stack将只会有当前task,其它task只能通过Recent键找回。
在 "singleTask" 的官方文档中,是这样描述的:
The system creates a new task and instantiates the activity at the root of the new task.
根据前面的内容可以知道,这里的描述是错误的。新Activity也是可能会在当前task中运行的(只要 taskAffinity 匹配即可)。 即使当前task不匹配新Activity的 taskAffinity,可能当前已经有一个 taskAffinity 指定的task, 那么新Activity也会在那个task中运行(而不是新建task)。