Android 中的 Activity Launch Mode 详解
Android 中的 Activity 有几种比较重要的启动模式,Standard\SingleTop\SingleTask\SingleInstance , 每一种启动模式有不同的使用场景, 网上也有许多分析这个的文章, 这里我以 Demo 的模式, 从 Activity 栈的角度来展示不同启动模式下的 Activity 的行为.
Activity 栈是一个先进后出的数据结构, 各位可以关注在每一步操作之后, 栈内容那一栏 , 可以更好地帮助理解不同的启动模式.
Demo 比较简单, 我也放到了 Github 上 , https://github.com/xiangjiana/emmmmm
, 有兴趣的可以自己跑一下 , 看看结果 , 只需要修改 StandardActivity 里面的跳转 Activity 就可以了.
Standard 标准模式
android:launchMode="standard"
最基本的模式,每次启动都会创建一个新的 Activity
// 1. 启动 Activity MainActivity //栈内容 com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2. 启动 StandardActivity MainActivity -> StandardActivity //栈内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3. 启动 StandardActivity MainActivity -> StandardActivity -> StandardActivity //栈内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity
SingleTop 栈顶复用模式
android:launchMode="singleTop"
如果当前 Activity 已经在栈顶,那么其 onNewIntent 会被调用;否则会重新创建 Activity
测试1 : SingleTopActivity 不在栈顶
// 1. 启动 MainActivity MainActivity //栈内容 com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2. 启动 StandardActivity MainActivity -> StandardActivity //栈内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3. 启动 SingleTopActivity MainActivity -> StandardActivity -> SingleTopActivity //栈内容 com.example.launchmodetest/.SingleTopActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 4. 启动 StandardActivity MainActivity -> StandardActivity -> SingleTopActivity -> StandardActivity //栈内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.SingleTopActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 5. 启动 SingleTopActivity: MainActivity -> StandardActivity -> SingleTopActivity -> StandardActivity -> SingleTopActivity //栈内容 com.example.launchmodetest/.SingleTopActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.SingleTopActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity //这里由于第三个 SingleTopActivity 不在栈顶,栈顶是 StandardActivity ,所以启动新的 SingleTopActivity 时会重新创建 SingleTopActivity
测试2 : SingleTopActivity 在栈顶
// 1. 启动 MainActivity MainActivity //栈内容 com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2. 启动 StandardActivity MainActivity -> StandardActivity //栈内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3. 启动 SingleTopActivity MainActivity -> StandardActivity -> SingleTopActivity //栈内容 com.example.launchmodetest/.SingleTopActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 4. 启动 SingleTopActivity MainActivity -> StandardActivity -> SingleTopActivity -> SingleTopActivity //栈内容 com.example.launchmodetest/.SingleTopActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity //SingleTopActivity 收到 am_new_intent ,而不是创建新的 Activity
SingleTask 栈内复用模式
android:launchMode="singleTask"
- 如果不加 Affinity , 那么 SingleTask 标记的 Activity 创建还是在当前的 Task 中
- SingleTask 标记的 Activity 是栈内复用模式,如果当前 Task 内没有这个 Activity,那么创建新的 Activity,如果当前 Task 内有这个 Activity,不管他在 Task 的哪个位置,都会直接复用这个 Activity (收到 onNewIntent)
- 如果栈内复用,那么会 Clear Task 中这个 Activity 上面的其他的 Activity
测试1:SingleTask(Without Affinity)
// 1. 启动 MainActivity: MainActivity //栈内容: com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2. 启动 StandardActivity MainActivity -> StandardActivity //栈内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3. 启动 SingleTaskActivity MainActivity -> StandardActivity -> SingleTaskActivity //栈内容: com.example.launchmodetest/.SingleTaskActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 4. 启动 StandardActivity MainActivity -> StandardActivity -> SingleTaskActivity -> StandardActivity //栈内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.SingleTaskActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 5. 启动 SingleTaskActivity MainActivity -> StandardActivity -> SingleTaskActivity -> StandardActivity -> SingleTaskActivity //栈内容: com.example.launchmodetest/.SingleTaskActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity //备注:SingleTaskActivity 收到 am_new_intent ,将其上面的 StandardActivity Clear 调
测试2:SingleTask(WithAffinity)
在 Manifest 中设置了 android:taskAffinity=”” 之后,启动 SingleTask 会启动一个新的 Task
// 1. 启动 MainActivity MainActivity //栈0内容: com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2. 启动 StandardActivity MainActivity -> StandardActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3. 启动 SingleTaskWithAffinity MainActivity -> StandardActivity -> SingleTaskWithAffinity //栈1内容: com.example.launchmodetest/.SingleTaskWithAffinity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 4. 启动 StandardActivity MainActivity -> StandardActivity -> SingleTaskWithAffinity -> StandardActivity //栈1内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.SingleTaskWithAffinity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 5. 启动 SingleTaskWithAffinity MainActivity -> StandardActivity -> SingleTaskWithAffinity -> StandardActivity -> SingleTaskWithAffinity //栈1内容: com.example.launchmodetest/.SingleTaskWithAffinity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity -------------------------------------------------------------------
总结
- 与 SingleTask 相比, SingleTaskWithAffinity 会创建新的 Stack
- 在 SingleTaskWithAffinity 启动 StandardActivity , 这个 StandardActivity 与 SingleTaskWithAffinity 在同一个栈
- 在栈 0 里面再启动 SingleTaskWithAffinity ,不会创建新的 Task
- 多任务里面会出现 SingleTaskWithAffinity
SingleInstance 单实例模式
android:launchMode="singleInstance"
单示例模式顾名思义,启动时,无论从哪里启动都会给 A 创建一个唯一的任务栈,后续的创建都不会再创建新的 A,除非 A 被销毁了
测试1:SingleInstance (Without Affinity)
taskAffinity=com.example.launchmodetest // 1\. 启动 MainActivity MainActivity //栈0内容 com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2\. 启动 StandardActivity MainActivity -> StandardActivity //栈0内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3\. 启动 SingleInstanceActivity MainActivity -> StandardActivity -> SingleInstanceActivity //栈1内容(多任务里面没有 Task) com.example.launchmodetest/.SingleInstanceActivity //栈0内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 4\. 启动 StandardActivity MainActivity -> StandardActivity -> SingleInstanceActivity -> StandardActivity //栈1内容(多任务里面没有 Task) com.example.launchmodetest/.SingleInstanceActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 5\. 启动 SingleInstanceActivity MainActivity -> StandardActivity -> SingleInstanceActivity -> StandardActivity -> SingleInstanceActivity //栈1内: (多任务里面没有 Task) com.example.launchmodetest/.SingleInstanceActivity //栈0内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 6\. 启动 StandardActivity MainActivity -> StandardActivity -> SingleInstanceActivity -> StandardActivity -> SingleInstanceActivity -> StandardActivity //栈1内: (多任务里面没有 Task) com.example.launchmodetest/.SingleInstanceActivity //栈0内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 7\. 启动 SingleInstanceActivity MainActivity -> StandardActivity -> SingleInstanceActivity -> StandardActivity -> SingleInstanceActivity -> StandardActivity -> SingleInstanceActivity //栈1内容 (多任务里面没有 Task) com.example.launchmodetest/.SingleInstanceActivity //栈0内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity
总结1
- SingleInstanceActivity 会创建新的 Task ,但是不会在多任务中出现
- SingleInstanceActivity 是全局唯一的,如果复用,其 onNewIntent 会被调用
- SingleInstanceActivity 启动新的 Activity,新的 Activity 不会在当前的 Task 里面,而是会回到上一个 Task 里面
测试2: SingleInstance (With Affinity)
在 Manifest 中设置了 android:taskAffinity=”” 之后,启动 SingleInstanceActivity 会出现在多任务中 ,其余的表现与没有设置 Affinity 一致
taskAffinity=null
// 1. 启动 MainActivity MainActivity //栈0内容: com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 2. 启动 StandardActivity MainActivity -> StandardActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 3. 启动 SingleInstanceWithAffinityActivity MainActivity -> StandardActivity -> SingleInstanceWithAffinityActivity //栈1内容:(多任务里面有 Task) com.example.launchmodetest/.SingleInstanceWithAffinityActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 4. 启动 StandardActivity MainActivity -> StandardActivity -> SingleInstanceWithAffinityActivity -> StandardActivity //栈1内容:(多任务里面有 Task) com.example.launchmodetest/.SingleInstanceWithAffinityActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 5. 启动 SingleInstanceWithAffinityActivity MainActivity -> StandardActivity -> SingleInstanceWithAffinityActivity -> StandardActivity -> SingleInstanceWithAffinityActivity //栈1内容:(多任务里面有 Task) com.example.launchmodetest/.SingleInstanceWithAffinityActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 6. 启动 StandardActivity MainActivity -> StandardActivity -> SingleInstanceWithAffinityActivity -> StandardActivity -> SingleInstanceWithAffinityActivity -> StandardActivity //栈1内容:(多任务里面有 Task) com.example.launchmodetest/.SingleInstanceWithAffinityActivity //栈0内容 com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity ------------------------------------------------------------------- // 7. 启动 SingleInstanceWithAffinityActivity MainActivity -> StandardActivity -> SingleInstanceWithAffinityActivity -> StandardActivity -> SingleInstanceWithAffinityActivity -> StandardActivity -> SingleInstanceWithAffinityActivity //栈1内容:(多任务里面有 Task) com.example.launchmodetest/.SingleInstanceWithAffinityActivity //栈0内容: com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.StandardActivity com.example.launchmodetest/.MainActivity
一些概念
TaskAffinity
taskAffinity=null
与 Activity 有着亲和关系的任务。从概念上讲,具有相同亲和关系的 Activity 归属同一Task(从用户的角度来看,则是归属同一“ Application ”)。 Task 的亲和关系由其根 Activity 的亲和关系确定。
亲和关系确定两件事 - Activity 更改到的父项 Task(请参阅 allowTaskReparenting 属性)和通过 FLAG_ACTIVITY_NEW_TASK 标志启动 Activity 时将用来容纳它的 Task。
默认情况下,应用中的所有 Activity 都具有相同的亲和关系。您可以设置该属性来以不同方式组合它们,甚至可以将在不同应用中定义的 Activity 置于同一 Task 内。 要指定 Activity 与任何 Task 均无亲和关系,请将其设置为空字符串。
如果未设置该属性,则 Activity 继承为应用设置的亲和关系(请参阅 元素的 taskAffinity 属性)。 应用默认亲和关系的名称是 元素设置的软件包名称。
ActivityRecord、TaskRecord、ActivityStack 之间的关系
- 一个 ActivityRecord 对应一个 Activity 实例,保存了一个 Activity 的所有信息 ; 但是一个 Activity可能会有多个 ActivityRecord ,因为 Activity 可以被多次启动,这个主要取决于其启动模式。
- 一个 TaskRecord 由一个或者多个 ActivityRecord 组成,这就是我们常说的任务栈,具有后进先出的特点
-
ActivityStack 则是用来管理 TaskRecord 的,包含了多个 TaskRecord
- 一般地,对于没有分屏功能以及虚拟屏的情况下,ActivityStackSupervisor 与ActivityDisplay 都是系统唯一;
- ActivityDisplay 主要有 Home Stack 、 App Stack、Recents Stack 这三个栈;
- 每个 ActivityStack 中可以有若干个 TaskRecord 对象;
- 每个 TaskRecord 包含如果若干个 ActivityRecord 对象;
- 每个 ActivityRecord记 录一个 Activity 信息。
下面是一个 dump 的例子,可以看到当前手机的 ActivityRecord、TaskRecord、ActivityStack
(adb shell dumpsys activity containers)
Activity 的几种类型
/** Activity type is currently not defined. */ public static final int ACTIVITY_TYPE_UNDEFINED = 0; /** Standard activity type. Nothing special about the activity... */ public static final int ACTIVITY_TYPE_STANDARD = 1; /** Home/Launcher activity type. */ public static final int ACTIVITY_TYPE_HOME = 2; /** Recents/Overview activity type. There is only one activity with this type in the system. */ public static final int ACTIVITY_TYPE_RECENTS = 3; /** Assistant activity type. */ public static final int ACTIVITY_TYPE_ASSISTANT = 4;