Skip to content

ycdev-demo/ActivityTaskDemo

Repository files navigation

ActivityTaskDemo

1. Demo app

在 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。

2. 总结

💡

可以使用如下命令来查看当前的 Activity 和 task 信息:

$ adb shell dumpsys activity package me.ycdev.android.demo.activitytask
  • 注1:看命令输出的 Running activities 部分即可。

  • 注2:本文测试环境为 Anroid 8.1.0 原生系统。

当 Activity A 启动 Activity B 时,需要回答如下问题:

  • Activity B在哪个task中运行?

  • Activity B是新建实例还是复用已有实例?

2.1. android:launchMode 角度

在不使用 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。

  • 注1:通过onNewIntent复用或者新建Activity B实例。

[any]

singleTask

Activity B 会在它的 android:taskAffinity 指定的task中运行(可以是当前task)。

  • 注1:如果Activity B已经有实例存在,那么它的task中之上的Activity会被全部清除(如果存在的话), 并通过onNewIntent复用该Activity B实例(因为,singleTask的Activity只能有一个实例)。

💡
由于 singleTask 和 singleInstance 运行在指定的task中,为了便于用户在任何时候 能够回到之前的工作,它们应该只用于 launcher Activity。

2.2. Intent flags 角度

使用 Intent flags 启动Activity,情况较为复杂。

2.2.1. FLAG_ACTIVITY_NEW_TASK

这个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

  • 首先,使用 Activity B 的 android:taskAffinity 去查找task,如果task存在:

    • 如果task中启动的第一个Activity(stack最下面)是Activity B,则只是把该task切换到前台;

    • 否则,根据Activity B的 android:launchMode 来决定如何启动Activity B(复用或者新建)。

  • 如果没有与 Activity B 的 android:taskAffinity 匹配的task,则新建task。

[any]

singleInstance

与不加flag的情况相同

[any]

singleTask

与不加flag的情况相同

2.2.2. FLAG_ACTIVITY_SINGLE_TOP

这个flag比较简单,相当于临时把 Activity B 的 android:launchMode 变成了 singleTop。

2.2.3. FLAG_ACTIVITY_CLEAR_TOP

用于清除目标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 会被选中。

2.2.4. FLAG_ACTIVITY_CLEAR_TASK

仅当目标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 。

2.3. 其它manifest属性

2.3.1. android:allowTaskReparenting

这个属性可以让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是无法触发这个功能的。

2.3.2. android:alwaysRetainTaskState

在某些特殊情况下,当用户从launcher重新打开一个app时,系统会清空已有task(如果存在的话)。 例如,当用户离开这个task超过了指定的时间(如30分钟)。 如果想你的app始终回到之前的task(即不允许系统清空task),那么可以把该属性设置为true(默认值为false)。 这是一个用户体验方面的设计,更改前跟PM好好讨论下。

该属性仅针对task的Root Activity有效,默认值false。

2.3.3. android:clearTaskOnLaunch

该属性与 android:alwaysRetainTaskState 相反。当task回到前台时, task中的Activity会被清空,仅留下Root Activity。

该属性仅针对task的Root Activity有效,默认值false。

2.3.4. android:finishOnTaskLaunch

该属性与 android:clearTaskOnLaunch 类似,但仅针对该Activity。 当Activity所在task被切换回前台时,该Activity会自动finish。

默认值false。

💡
需要使用 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 切换回task,才会触发这个特性。

2.4. task 与 back stack

当多个Activity被依次启动后,可能存在多个task。此时,如果一直按back键退出, 那么Activity的退出顺序是这样的(跟启动顺序可能不同),一个task一个task退出: task1[Activity1, Activity2,…​],task2[ActivityA,ActivityB]…​

如果按Home键退回到了桌面或者仅是按了一下Recent键,再回到先前的task, 那么此时的back stack将只会有当前task,其它task只能通过Recent键找回。

2.5. 官方文档解读

2.5.1. android:launchMode 之 "singleTask"

在 "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)。

2.5.2. android:taskAffinity

按官方文档,不同apps的Activity可以使用相同的 android:taskAffinity(没测试过,也不知道有什么应用场景)。 这意味着 android:taskAffinity 会在整个系统层面来工作,而不是作用于app内部。 所以,在自定义Activity的 android:taskAffinity 时,最好用包名作为前缀, 以避免跟其它apps中的定义冲突。

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages