Android Task 、 TaskRecord 和启动模式之学习笔记

一 . Task 、TaskRecord 简介

1 . Task :即任务,是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个 TaskRecord 中。

2  . TaskRecord :即任务栈,或者叫返回堆栈 ( back Stack ) ,是一种栈的数据结构,按照“后进先出”的规则管理着其中的元素。换言之,当用户打开一个应用,并在该应用中启动了一系列的Activity ,这些Activity 默认情况下是都放着一个任务栈中,按照栈的结构类型对这些 Activity 进行统一的管理。从用户的角度可以粗略的理解为一个Task 对应一个 “应用程序” ,但是从 android 系统的角度来说,一个Task 对应一个或多个”应用程序“。

二 . Task 的创建

       当用户在Launcher 点击应用图标的时候,系统首先会创建一个Task name 为应用包名的一个Task ,并把该应用的默认主Activity 放入Task 的栈底,之后如果还打开别的Activity ,默认情况下,会依次将打开的一系列Activity 依次压入 TaskRecord 中;如果这时候用户按下Home 键,则这个Task 会从前台切换到后台,处于后台时,该 TaskRecord 中的Activity 的排序以及状态都会被保存下来。如果此时用户打开另外一个新的应用,默认情况下,这个新的应用期望加入到 Task name 为自己包名的Task 中,如果该Task 不存在,则创建一个新的Task ,并且该Task 的默认 name 和该应用的包名相同,如果期望的 Task 存在,则直接复用原有的 Task ,如果该 Task 处于后台,则需要把整个Task 转移到前台。

       大多数任务都从设备主屏幕上启动。当用户轻触应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务就会转到前台运行。如果该应用没有任务存在(应用最近没有使用过),则会创建一个新的任务,并且该应用的默认“主”Activity 将会作为 TaskRecord 的根 Activity 打开。

       在当前 Activity 启动另一个 Activity 时,新的 Activity 将被推送到 TaskRecord 顶部并获得焦点。上一个 Activity 仍保留在堆栈中,但会停止。当 Activity 停止时,系统会保留其界面的当前状态。当用户按 Back 按钮时,当前 Activity 会从 TaskRecord 顶部退出(该 Activity 销毁),上一个 Activity 会恢复(界面会恢复到上一个状态)。TaskRecord 中的 Activity 永远不会重新排列,只会被送入和退出,在当前 Activity 启动时被送入堆栈,在用户使用 Back 按钮离开时从 TaskRecord 中退出。因此,TaskRecord 按照“后进先出”的对象结构运作。图 3 借助一个时间轴直观地显示了这种行为。该时间轴显示了 Activity 之间的进展以及每个时间点的当前 TaskRecord。

       如果用户继续按 Back 按键,则 TaskRecord 中的 Activity 会逐个退出,以显示前一个 Activity,直到用户返回到主屏幕(或 Task 开始时运行的 Activity)。移除 TaskRecord 中的所有 Activity 后,该 Task 将不复存在。

 Android Task 、 TaskRecord 和启动模式之学习笔记_第1张图片

图 3. 有关 Task 中的每个新 Activity 如何添加到 TaskRecord 的图示。当用户按 Back 按钮时,当前 Activity 会销毁,上一个 Activity 将恢复。 

2 . 1  提示:Home 键和 Back 键 的区别

Home 键:当前 Activity 会处于暂停状态,但没有完全销毁

Back 键:把当前 Activity 从返回 TaskRecord 中取出,并销毁掉。

三 . Task 的前后台切换

        Task 是一个整体单元,当用户开始一个新 Task 或通过主屏幕按钮进入主屏幕时,Task 可移至“后台”。在后台时,Task 中的所有 Activity 都会停止,但Task 的 TaskRecord 会保持不变,当其他 Task 启动时,当前 Task 只是失去了焦点,如图 4 所示。这样一来,Task 就可以返回到“前台”,以便用户可以从他们离开的地方继续操作。举例来说,假设当前 Task( Task A )的 TaskRecord 中有 3 个 Activity,当前 Activity 下有 2 个 Activity。用户按主屏幕按钮,然后从应用启动器中启动新应用。主屏幕出现后,Task A 转到后台。当新应用启动时,系统会启动该应用的 Task( Task B ),该 Task 具有自己的 TaskRecord 。与该应用互动后,用户再次返回到主屏幕并选择最初启动Task A 的应用。现在,Task A 进入前台,其 TaskRecord 中的所有三个 Activity 的状态都完好如初,TaskRecord 顶部的 Activity 恢复运行。此时,用户仍可通过以下方式切换到 Task B:转到主屏幕并选择启动该 Task 的应用图标(或者从最近使用的应用屏幕中选择该应用的 Task)。

Android Task 、 TaskRecord 和启动模式之学习笔记_第2张图片

图 4. 两个Task:Task B 在前台接收用户互动,Task A 在后台等待恢复。

四 . Task 的默认行为

       由于TaskRecord 中的 Activity 不会被重新排列,如果应用允许用户从多个 Activity 启动特定的 Activity,系统便会创建该 Activity 的新实例并将其推送到 TaskRecord 中(而不是将该 Activity 的某个先前的实例移至 TaskRecord 顶部)。这样一来,应用中的一个 Activity 就可能被多次实例化(甚至是从其他 Task 对其进行实例化),如图 5 所示。因此,如果用户使用 Back 按钮向后导航,Activity 的每个实例将按照它们被打开的顺序显示出来(每个实例都有自己的界面状态)。

Android Task 、 TaskRecord 和启动模式之学习笔记_第3张图片

 图 5. 单个 Activity 会被多次实例化

五 . 定义启动模式——(改变 Task 的行为模式)  

       Task 的默认行为会导致同个Activity 被实例化多次,如果想改变这种默认的方式,可以通过定义启动模式来确定 Activity 的新实例如何与当前的 Task 关联,下面两种方式可以定义不同的启动模式:

5 . 1  使用清单文件  

       在 AndroidManifest.xml 清单文件中的 < activity > 元素中配置相应的属性值,其中,< activity > 主要的属性包括:

下面对每个属性进行逐一介绍:

(1) taskAffinity:亲和性

       表示 Activity 倾向于属于哪个 Task 。从概念上讲,具有同一亲和性的 Activity 归属同一 Task(从用户的角度来看,则是归属同一“应用”)。Task 的亲和性由其根 Activity 的亲和性确定。

       默认情况下,同一应用中的所有 Activity 都具有同一亲和性。同时可以修改 Activity 的默认亲和性。在不同应用中定义的 Activity 可以具有相同的亲和性,或者在同一应用中定义的 Activity 也可以被指定不同的 Task 亲和性。如果将其设置为空字符串,则说明指定 Activity 与任何 Task 均无相似性。

提示:一般不建议把 android:taskAffinity="  " ,即设置为空字符串,因为这样会导致应用安装失败。

       taskAffinity 属性采用字符串值,因为系统默认亲和性的名称为 元素所设置的软件包名称,所以设置该值时必须不同于软件包名。

(2) allowTaskReparenting

       当下一次将启动 Activity 的 Task 转至前台时,Activity 是否能从该 Task 转移至与其有相似性的 Task 。“true” :表示可以转移;“false” :表示仍须留在启动它的任务处。

       如果未设置该属性,则对 Activity 应用由 < application > 元素的相应 allowTaskReparenting 属性所设置的值。默认值为“false”。

       正常情况下,Activity 启动时会与启动它的 Task 关联,并在其整个生命周期中一直留在该 Task 处。当不再显示现有 Task 时,您可以使用该属性强制 Activity 将其父项更改为与其有相似性的 Task 。该属性通常用于将应用的 Activity 转移至与该应用关联的主 Task 。

       例如,如果电子邮件消息包含网页链接,则点击该链接会调出可显示该网页的 Activity。该 Activity 由浏览器应用定义,但作为电子邮件 Task 的一部分启动。如果将该 Activity 的父项更改为浏览器Task ,则它会在浏览器下一次转至前台时显示,在电子邮件 Task 再次转至前台时消失。

(3) clearTaskOnLaunch

       每当从主屏幕重新启动 Task 时,是否都从该 Task 中移除根 Activity 之外的所有 Activity 。“true”:表示始终将 Task 清除至只剩其根 Activity;“false”:表示不清除。默认值为“false”。该属性只对启动新 Task 的 Activity(根 Activity)有意义;Task 中的所有其他 Activity 均可忽略该属性。

       若值为“true”,则每次当用户再次启动 Task 时,无论用户最后在 Task 中正在执行哪个 Activity,也无论用户是使用 Back 按钮还是主屏幕按钮离开,系统都会将用户转至 Task 的根 Activity。分析如下图:

 Android Task 、 TaskRecord 和启动模式之学习笔记_第4张图片 

  (4)alwaysRetainTaskState

       系统是否始终保持 Activity 所在 Task 的状态 。“true”:表示是;“false”:表示允许系统在特定情况下将 Task 重置到其初始状态(即Task 中只有根 Activity )。默认值为“false”。该属性只对 Task 的根 Activity 有意义;所有其他 Activity 均可忽略该属性。

       正常情况下,当用户从主屏幕重新选择某个 Task 时,系统会在特定情况下清除该 Task (从根 Activity 上的 TaskRecord 中移除所有 Activity )。通常,如果用户在一段时间(如 30 分钟)内未访问 Task ,系统会执行清除操作。

       不过,如果该属性的值是“true”,则无论用户如何返回 Task ,该 Task 始终会显示最后一次的状态。

(5)   finishOnTaskLaunch

       每当用户再次启动 Activity 的 Task(在主屏幕上选择Task )时,是否应关闭现有的 Activity 实例。“true”:表示应关闭;“false”:表示不应关闭。默认值为“false”。

       如果此属性和 allowTaskReparenting 均为“true”,则优先使用此属性。系统会忽略 Activity 的相似性。系统不会更改 Activity 的父项,而是将其销毁。

(6)launchMode

       该属性是关于如何启动 Activity ,其中有四种模式可与 Intent 对象中的 Activity 标记协同工作,一同确定在调用 Activity 处理 Intent 时应执行的操作。四种模式如下:

1)standard ——(默认模式)

       每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例之前是否已经存在。

 

2)singleTop

       栈顶复用模式。该模式下,如果待启动的 Activity 已经位于Task 的栈顶,则该 Activity 的实例不会被创建,而是直接复用已有的实例,同时该 Activity 的 onNewIntent( ) 方法会被回调,这个 Activity 的onCreat( )、onStart( ) 方法不会被系统调用;如果待启动的 Activity 的实例不是位于Task 的栈顶,那么系统会为这个 Activity 创建新的实例,并将其放入 Task 中。

3)singleTask

       栈内复用模式。这是一种单实例模式,在该模式下,如果 Activity 已经在 Task 栈中存在,那么之后多次启动该 Activity ,系统都不会为其创建新的实例。具体来说还应该分成几种场景:第一种场景是该 Activity 已经位于Task 栈顶,则直接复用这个实例,并调用其 onNewIntent( ) 方法;第二种场景是该 Activity 并非位于 Task 栈顶,则把该 Activity 之上的所有 Activity 都移出 Task 栈,接着再调用该 Activity 的 onNewIntent( ) 方法;第三中场景是该 Activity 实例不存在 Task 中,此时系统会比较调用方 Activity 和 该 Activity 的 taskAffinity 是否一致,如果一致就将该 Activity 创建一个实例,并加入到已存在的 Task 中;如果不一致,则为该 Activity 创建一个新的 Task ,此时,该 Activity 将作为 根 Activity 加入到新 Task 中。

       例子分析:假设 appA 应用包括 Activity Y 和 Activity X ,并且在 元素中指定 android:launchMode= "singleTask" ,来声明该 appA应用始终在它自己的 Task 中打开,这意味着,如果在其他应用中,如下图中的 Activity 2 启动 appA ,此时系统不会将 Activity X 放入当前的 Task ,而是创建一个新的 Task ;如果此前 appA  的 Task 已存在于后台,则会将该 Task 从后台状态转为前台状态,接着在启动 Activity Y ,具体情况如下如所示:

 Android Task 、 TaskRecord 和启动模式之学习笔记_第5张图片 

图 7. 采用“singleTask”启动模式的 Activity 添加到 TaskRecored 的过程图示。如果 Activity 已经存在于某个具有自己的 TaskRecord 的后台 Task 中,那么整个 TaskRecord 也会转到前台,覆盖当前 Task 。

4)singleInstance

       单实例模式。该模式下,Task 栈里面只能有一个 Activity 。具体而言,就是当 Activity 被启动后,系统会为它创建一个新的 Task 栈,然后把该 Activity 的实例压入该 Task 栈中,并且该 Task 栈不允许其他 Activity 压入其中。该 Activity 实例是 Task 中唯一的 Activity。

5 . 2  使用 Intent 标记 ——(优先级比使用清单文件高)

        当启动 Activity 时,通过给 startActivity ( ) 的 intent 中添加相应的标记来修改 Activity 与其 Task 的默认关联。

intent 标记包括:

(1)FLAG_ACTIVITY_NEW_TASK:( flag_activity_new_task )

       在新 Task 中启动 Activity 。如果现在启动的 Activity 已有 Task 在运行,则系统会将该 Task 转到前台并恢复其最后的状态,而 Activity 将在 onNewIntent ( ) 中收到新的 Intent 。

行为等同于 android:launchMode=" singleTask " 。

(2)FLAG_ACTIVITY_SINGLE_TOP:( flag_activity_single_top )

       如果要启动的 Activity 是当前位于 TaskRecord 栈顶,则现有实例会收到 onNewIntent( ) 的调用,而不会创建 Activity 的新实例。

行为等同于 android:launchMode=" singleTop " 。

(3)FLAG_ACTIVITY_CLEAR_TOP : ( flag_activity_clear_top )

       如果要启动的 Activity 已经在当前 Task 中运行,则不会创建该 Activity 的新实例,而是会销毁位于它之上的所有其他 Activity ,并通过 onNewIntent( ) 将此 intent 传送给它的已恢复实例,此时,该 Activity 位于 TaskRecord 顶部。

launchMode 属性没有可产生此行为的值。

      该属性最常和 FLAG_ACTIVITY_NEW_TASK 结合使用,表示可以查找其他 Task 中的现有 Activity ,并将其置于能够相应 intent 的位置。

你可能感兴趣的:(Android,android,java,apache)