Skip to content

ycdev-demo/ActivityTaskDemo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

e97101a · Aug 8, 2020

History

4 Commits
Aug 1, 2020
Aug 8, 2020
Aug 1, 2020
Aug 1, 2020
Aug 1, 2020
Aug 8, 2020
Aug 3, 2020
Aug 8, 2020
Aug 3, 2020
Aug 8, 2020
Aug 1, 2020
Aug 1, 2020
Aug 1, 2020
Aug 3, 2020

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