转载请注明出处:www.leoyanblog.com
本文出自 LeoYan 的博客
本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。
Activity的任务和启动模式
任务与返回栈
后进先出原则
Task 就好像是能包含很多 Activity 的栈。默认情况下,一个 Activity 启动另外一个 Activity 时,两个 Activity 是放在同一个 Task 栈中的,第二个 Activity 压入第一个 Activity 所在的 Task 栈。当用户按“返回”按钮时,第二 Activity 从栈中弹出,第一个 Activity 恢复执行。直到用户返回主屏幕为止(或者,返回 Task 中正在运行的任意 Activity)。当所有 Activity 均从堆栈中移除后,Task 即不复存在。
多任务
Task 是一个有机整体,当用户开始新 Task 或通过“主页”按钮转到主屏幕时,可以移动到“后台”。 尽管在后台时,该任务中的所有 Activity 全部停止,但是 Task 的返回栈仍旧不变,也就是说,当另一个 Task 发生时,该 Task 仅仅失去焦点而已,如上图中所示。然后, Task 可以返回到“前台”,用户就能够回到离开时的状态。
注:后台可以同时运行多个 Task 。但是,如果用户同时运行多个后台 Task ,则系统可能会开始销毁后台 Activity,以回收内存资源,从而导致 Activity 状态丢失。
可多次实例化
应用中的一个 Activity 可能会多次实例化,如上图所示。因此,如果用户使用“返回”按钮向后导航,则会按 Activity 每个实例的打开顺序显示这些实例(每个实例的 UI 状态各不相同)。
任务的默认行为总结
- 当 Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。
- 用户通过按“主页”按钮离开任务时,当前 Activity 将停止且其任务会进入后台。 系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。
- 如果用户按“返回”按钮,则当前 Activity 会从堆栈弹出并被销毁。 堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统不会保留该 Activity 的状态。
- 即使来自其他任务,Activity 也可以多次实例化。
LaunchMode简介
LaunchMode 是 Activity 类的一个属性,该属性包括4个具体值:standard、singleTop、singleTask、singleInstance。我们都知道,Android 系统中启动 Activity 是通过 Intent 实例进行的,当系统收到一个 Intent 的实例需要去启动指定的 Activity 的时候,Android 系统会根据目标 Activity 的该属性值来决定是要要创建新的该 Activity 实例以及如何在 Task 中创建该 Activity 的实例。这就是 LaunchMode 这个属性的作用。
LaunchMode 分为两个类别
- 普通类型
用户常用的启动模式类型,大部分 Activity都是这两种启动模式。包括 standard 和 singleTop 两种启动模式
- 特殊类型
具有特殊的行为的启动模式,只针对特殊需求的使用。包括 singleTask 和 singleInstance 两种启动模式
官方文档对4中启动模式描述如下:
使用场景 | 启动模式 | 是否可以有多个实例? | 简介 |
---|---|---|---|
普通类型 | standard | 是 | 系统默认的启动模式,当系统接收到一个 Intent 实例去启动一个 standard 模式的 Activity 时,系统总是会在目标栈的顶部创建一个新的 Activity 实例,并把 Intent 的实例传进去。 |
普通类型 | singleTop | 视具体情况 | 当系统接收到一个 Intent 实例去启动一个 singleTop 模式的 Activity 时,如果在目标栈的顶部存在一个该 Activity 的实例的话,那么系统就会重用这个 Activity 的实例而不创建新的实例,并回调该 Activity 的 onNewIntent(Intent intent) 方法把新的 Intent 实例当作方法参数传递进去;如果在目标栈的顶部没有该 Activity 的实例的话系统将会在新建一个 Activity 实例,与 standard 的行为就一样了。 |
特殊类型 | singleTask | 否 | 当系统接收到一个 Intent 实例去启动一个 singleTask 模式的 Activity 时,如果不存在该 Activity 的实例的话,系统会先创建一个新的 Task,并在该 Task 底部里面创建一个该 Activity 的实例,随后把 Intent 实例传递进去;如果已经存在一个该 Activity 的实例的话,系统就不会再创建新的实例,那么系统就会重用这个 Activity 的实例而不创建新的实例,并回调该 Activity 的 onNewIntent(Intent intent) 方法把新的 Intent 实例当作方法参数传递进去,同时,该 Activity 实例所在的 Task 将会被调到前台。 |
特殊类型 | singleInstance | 否 | 类似于 singleTask ,唯一不同的地方在于,singleInstance 的 Activity 不允许自己的 Task 中存在其他的 Activity 实例,也就是说 singleInstance 的 Activity 永远是 Task 中唯一的一个 Activity 实例。 |
LaunchMode 的定义
- 静态定义:在目标 Activity 的 mainfest 中使用标签静态定义
- 动态定义:在启动目标 Activity 的 Intent 对象中使用不同的 flag 定义,可以用来定义 LaunchMode 的 flag 有以下三个:
flag | 对应的 LaunchMode |
---|---|
FLAG_ACTIVITY_NEW_TASK | singleTask |
FLAG_ACTIVITY_SINGLE_TOP | singleTop |
FLAG_ACTIVITY_CLEAR_TOP | 无对应的LaunchMode |
注:在Activity A启动 Activity B的时候,假如B已经在 mainfest 文件中定义过 LaunchMode,此时的 Intent 对象中也有定义 LaunchMode,这时候以 Activity A start B 的时候以 Intent 中定义的为准。
典型使用场景
LaunchMode | 使用场景 |
---|---|
standard | 绝大多数标准的跳转 |
singleTop | 新闻阅读类的内容页面,假如在一个新闻页面又打开了另一个新闻页面,这时候Task的顶端已经有该Activity的实例了,就不用再初始化了 |
singleTask | 通知栏启动其他app,一般都会在Intent中加上FLAG_ACTIVITY_NEW_TASK,相当于使用了singleTask |
一些会影响 Task 行为的 mainfest 中 Activity 的属性
属性名称 | 可取值 | 意义 |
---|---|---|
android:taskAffinity | 一个存在的包名 | 表示该 Activity 与该包名具有亲缘,在该 Activity 启动的时候,会优先选择被添加进已经存在的具有亲缘关系的 Task 中。 |
android:allowTaskReparenting | true, false | 如果一个 Activity 的该属性值是 true,它先被一个外部 app 启动了,此时它存在于那个调用它的 app 的 Task 中,当与它有亲缘关系的 Task 被后台带回到前台的时候,系统会把它换到该 Task 中;如果值为 false 的话,就不会发生移动。 |
android:alwaysRetainTaskState | true, false | 正常情况下,当一个 Task 在后台待了很久之后,系统会把该 Task 的非根 Activity都清除,只保留根 Activity。但是假如根 Activity 的该属性值被设置为 true 的话,系统会保留该 Task 中所有的 Activity。 |
LaunchMode 总结
LaunchMode 的作用,是让开发者可以在一定程度上决定自己的 Activity 该如何被系统实例化,从而来满足开发者的开发需求。
LaunchMode 一共分为两类,一类是普通开发者最常用的:standard 和 singleTop,其中 standard 是官方定义的最常见的也是默认的系统对 Activity 进行实例化的行为模式,singleTop 在 standard 的基础上进行了点小的改动,当目标栈的顶部有 Activity 的实例的时候将不再对该 Activity 进行初始化而是直接使用现成的 Activity 实例,因为有很多的使用场景是 standard 不太合适而 singleTop 比较合适的;另一个类别的是普通开发者不常使用的,singleTask 和 singleInstance,官方并不推荐在通常的场景中使用该类的 LaunchMode,只有在极少数特殊的情形中才去使用,该类 LaunchMode 的特点是 Activity 在系统中只会有一个实例存在,不同的地方在于 singleInstance 的 Activity 会独占一个 Task,而 singleTask 的则不会。
LaunchMode 可以在 mainfest 文件中静态地设置,同时,在代码中,通过 Intent 的几个 flag 也可以达到设置 LaunchMode 的目的。假如某一次启动一个 Activity 的时候,在 Intent 中指定了一个 LaunchMode,而且该 Activity 本身已经在 mainfest 文件中设置了 LaunchMode,那么这个时候系统会 以Intent 中指定的为准。Android 系统这样设计的目的,我想是为了让整个系统的 Activity 可以更方便地让其它 Activity(可以不再一个app内)进行调用,这与 Android 的开放的态度是一致的。