android activity与任务原理解析及设置

来源:互联网 发布:约瑟夫环递归java 编辑:程序博客网 时间:2024/06/06 16:05
一个 activity 可以启动另外一个,甚至包括与它不处于同一应用程序之中的。举个例子说,假
设你想让用户看到某个地方的街道地图。而已经存在一个具有此功能的 activity 了,那么你的 activity 所
需要做的工作就是把请求信息放到一个 Intent 对象里面,并把它传递给 startActivity()。于是地图浏览器
就会显示那个地图。而当用户按下 BACK 键的时候,你的 activity 又会再一次的显示在屏幕上。
对于用户来说,这看起来就像是地图浏览器是你 activity 所在的应用程序中的一个组成部分,其实它是在
另外一个应用程序中定义,并运行在那个应用程序的进程之中的。Android 将这两个 activity 放在同一个
任务中来维持一个完整的用户体验。简单的说,任务就是用户所体验到的“应用程序”。它是安排在一个堆
栈中的一组相关的 activity。堆栈中的根 activity 就是启动了这整个任务的那个──一般情况下,它就是用
户在应用程序加载器中所选择的。而堆栈最上方的 activity 则是当前运行的──用户直接对其进行操作的。
当一个 activity 启动另外一个的时候,新的 activity 就被压入堆栈,并成为当前运行的 activity。而前一
个 activity 仍保持在堆栈之中。当用户按下 BACK 键的时候,当前 activity 出栈,而前一个恢复为当前运
行的 activity。
堆栈中保存的其实是对象,所以如果发生了诸如需要多个地图浏览器的情况,就会使得一个任务中出现多
个同一 Activity 子类的实例同时存在,堆栈会为每个实例单独开辟一个入口。堆栈中的 Activity 永远不会
重排,只会压入或弹出。
任务其实就是 activity 的堆栈,而不是 manifest 文件中的一个类或者元素。所以你无法撇开 activity 而
为一个任务设置一个值。而事实上整个任务使用的值是在根 activity 中设置的。比如说,下一节我们会谈
及“任务的 affinity”,从 affinity 中读出的值将会设置到任务的根 activity 之中。
任务中的所有 activity 是作为一个整体进行移动的。整个的任务(即 activity 堆栈)可以移到前台,或退
至后台。举个例子说,比如当前任务在堆栈中存有四个 activity──三个在当前 activity 之下。当用户按下
HOME 键的时候,回到了应用程序加载器,然后选择了一个新的应用程序(也就是一个新任务)。则当前
任务遁入后台,而新任务的根 activity 显示出来。然后,过了一小会儿,用户再次回到了应用程序加载器
而又选择了前一个应用程序(上一个任务)。于是那个任务,带着它堆栈中所有的四个 activity,再一次
的到了前台。当用户按下 BACK 键的时候,屏幕不会显示出用户刚才离开的 activity(上一个任务的根
activity)。取而代之,当前任务的堆栈中最上面的 activity 被弹出,而同一任务中的上一个 activity 显
示了出来。
上述的种种即是 activity 和任务的默认行为模式。但是有一些方法可以改变所有这一切。activity 和任务
的联系、任务中 activity 的行为方式都被启动那个 activity 的 Intent 对象中设置的一系列标记和 manifest
文件中那个 activity 中的<activity>元素的系列属性之间的交互所控制。无论是请求发出者和回应者在这
里都拥有话语权。
我们刚才所说的这些关键 Intent 标记如下:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
而关键的<activity>属性是:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
接下来的一节会描述这些标记以及属性的作用,它们是如何互相影响的,以及控制它们的使用时必须考虑
到的因素。
Affinity(吸引力)和新任务
默认情况下,一个应用程序中的 activity 相互之间会有一种 Affinity──也就是说,它们首选都归属于一个
任务。然而,可以在 <activity>元素中把每个 activity 的 taskAffinity 属性设置为一个独立的 affinity。
于是在不同的应用程序中定义的 activity 可以享有同一个 affinity,或者在同一个应用程序中定义的
activity 有着不同的 affinity。affinity 在两种情况下生效:当加载 activity 的 Intent 对象包含了
FLAG_ACTIVITY_NEW_TASK 标记,或者当 activity 的 allowTaskReparenting 属性设置为“true”。
FLAG_ACTIVITY_NEW_TASK 标记
如前所述,在默认情况下,一个新 activity 被另外一个调用了 startActivity()方法的 activity 载入了任务
之中。并压入了调用者所在的堆栈。然而,如果传递给 startActivity()的 Intent 对象包含了
FLAG_ACTIVITY_NEW_TASK 标记,系统会为新 activity 安排另外一个任务。一般情况下,如同标记所
暗示的那样,这会是一个新任务。然而,这并不是必然的。如果已经存在了一个与新 activity 有着同样
affinity 的任务,则 activity 会载入那个任务之中。如果没有,则启用新任务。
allowTaskReparenting 属性
如果一个 activity 将 allowTaskReparenting 属性设置为“true”。它就可以从初始的任务中转移到与其拥
有同一个 affinity 并转向前台的任务之中。比如说,一个旅行应用程序中包含的预报所选城市的天气情况
的 activity。它与这个应用程序中其它的 activity 拥有同样的 affinity (默认的 affinity)而且允许重定父
级。你的另一个 activity 启动了天气预报,于是它就会与这个 activity 共处与同一任务之中。然而,当那
个旅行应用程序再次回到前台的时候,这个天气预报 activity 就会被再次安排到原先的任务之中并显示出
来。
如果在用户的角度看来,一个.apk 文件中包含了多于一个的“应用程序”,你可能会想要为它们所辖的
activity 安排不一样的 affinity。
加载模式
<activity>元素的 launchMode 属性可以设置四种不同的加载模式:
"standard" (默认值)
"singleTop"
"singleTask"
"singleInstance"
这些模式之间的差异主要体现在四个方面:
• 哪个任务会把持对 intent 做出响应的 activity。对“standard”和“singleTop”模式而言,是产
生 intent(并调用 startActivity())的任务──除非 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 标
记。而在这种情况下,如同上面 Affinitie 和新任务一节所述,会是另外一个任务。
相反,对“singleTask”和“singleInstance”模式而言,activity 总是位于任务的根部。正是它们
定义了一个任务,所以它们绝不会被载入到其它任务之中。
• activity 是否可以存在多个实例。一个“standard”或“singleTop”的 activity 可以被多次初始
化。它们可以归属于多个任务,而一个任务也可以拥有同一 activity 的多个实例。
相反,对“singleTask”和“singleInstance”的 activity 被限定于只能有一个实例。因为这些
activity 都是任务的起源,这种限制意味着在一个设备中同一时间只允许存在一个任务的实例。
• 在实例所在的任务中是否会有别的 activity。一个“singleInstance”模式的 activity 将会是它
所在的任务中唯一的 activity。如果它启动了别的 activity,那个 activity 将会依据它自己的加
载模式加载到其它的任务中去──如同在 intent 中设置了 FLAG_ACTIVITY_NEW_TASK 标记一
样的效果。在其它方面,“singleInstance”模式的效果与“singleTask”是一样的。
剩下的三种模式允许一个任务中出现多个 activity。“singleTask”模式的 activity 将是任务的根
activity,但它可以启动别的 activity 并将它们置入所在的任务中。“standard”和
“singleTop”activity 则可以在堆栈的任意位置出现。
• 是否要载入新的类实例以处理新的 intent。对默认的"standard"模式来说,对于每个新 intent
都会创建一个新的实例以进行响应,每个实例仅处理一个 intent。“singleTop”模式下,如果
activity 位于目的任务堆栈的最上面,则重用目前现存的 activity 来处理新的 intent。如果它不
是在堆栈顶部,则不会发生重用。而是创建一个新实例来处理新的 intent 并将其推入堆栈。
举例来说,假设一个任务的堆栈由根 activityA 和 activity B、C 和位于堆栈顶部的 D 组成,即
堆栈 A-B-C-D。一个针对 D 类型的 activity 的 intent 抵达的时候,如果 D 是默认的“standard”
加载模式,则创建并加载一个新的类实例,于是堆栈变为 A-B-C-D-D。 然而,如果 D 的载入模
式为“singleTop”,则现有的实例会对新 intent 进行处理(因为它位于堆栈顶部)而堆栈保持 A-BC-D 的形态。
换言之,如果新抵达的 intent 是针对 B 类型的 activity,则无论 B 的模式是“standard”还是
“singleTop” ,都会加载一个新的 B 的实例(因为 B 不位于堆栈的顶部),而堆栈的顺序变为
A-B-C-D-B。
如前所述,“singleTask”或“singleInstance”模式的 activity 永远不会存在多于一个实例。所以
实例将处理所有新的 intent。一个“singleInstance”模式的 activity 永远保持在堆栈的顶部(因
为它是那个堆栈中唯一的一个 activity),所以它一直坚守在处理 intent 的岗位上。然而,对一
个“singleTask”模式的 activity 来说,它上面可能有,也可能没有别的 activity 和它处于同一堆
栈。在有的情况下,它就不在能够处理 intent 的位置上,则那个 intent 将被舍
弃。(即便在 intent 被舍弃的情况下,它的抵达仍将使这个任务切换至前台,并一直保留)
当一个现存的 activity 被要求处理一个新的 intent 的时候,会调用 onNewIntent()方法来将 intent 对
象传递至 activity。(启动 activity 的原始 intent 对象可以通过调用 getIntent()方法获得。)
请注意,当一个新的 activity 实例被创建以处理新的 intent 的时候,用户总可以按下 BACK 键来回到前
面的状态(回到前一个 activity)。但当使用现存的 activity 来处理新 intent 的时候,用户是不能靠按下
BACK 键回到当这个新 intent 抵达之前的状态的。
想获得更多关于加载模式的内容,请参阅 <activity> 元素的描述。
清理堆栈
如果用户离开一个任务很长一段时间,系统会清理该任务中除了根 activity 之外的所有 activity。当用户
再次回到这个任务的时候,除了只剩下初始化 activity 尚存之外,其余都跟用户上次离开它的时候一样。
这样做的原因是:在一段时间之后,用户再次回到一个任务的时候,他们更期望放弃他们之前的所作所为,
做些新的事情。
这些属于默认行为,另外,也存在一些 activity 的属性用以控制并改变这些行为:
alwaysRetainTaskState 属性
如果一个任务的根 activity 中此属性设置为“true”,则上述默认行为不会发生。任务将在很长的一段时间
内保留它堆栈内的所有 activity。
clearTaskOnLaunch 属性
如果一个任务的根 activity 中此属性设置为“true”,则每当用户离开这个任务和返回它的时候,堆栈都会
被清空至只留下 rootactivity。换句话说,这是 alwaysRetainTaskState 的另一个极端。哪怕仅是过了
一小会儿,用户回到任务时,也是见到它的初始状态。
finishOnTaskLaunch 属性
这个属性与 clearTaskOnLaunch 属性相似,但它仅作用于单个的 activity,而不是整个的 task。而且它
可以使任意 activity 都被清理,甚至根 activity 也不例外。当它设置为“true”的时候,此 activity 仅做为
任务的一部分存在于当前回话中,一旦用户离开并再次回到这个任务,此 activity 将不复存在。
此外,还有别的方式从堆栈中移除一个 activity。如果一个 intent 对象包含
FLAG_ACTIVITY_CLEAR_TOP 标记,而且目标任务的堆栈中已经存在了一个能够响应此 intent 的
activity 类型的实例。则这个实例之上的所有 activity 都将被清理以使它位于堆栈的顶部来对 intent 做出
响应。如果此时指定的 activity 的加载模式为“standard”,则它本身也会从堆栈中移除,并加载一个新
的实例来处理到来的 intent。这是因为加载模式为“standard”的 activity 总会创建一个新实例来处理新
的 intent。
FLAG_ACTIVITY_CLEAR_TOP 与 FLAG_ACTIVITY_NEW_TASK 经常合并使用。这时,这些标记提供
了一种定位其它任务中现存的 activity 并将它们置于可以对 intent 做出响应的位置的方法。
启动任务
当一个 activity 被指定一个“android.intent.action.MAIN”做为动作,以及
“android.intent.category.LAUNCHER”做为类别的 intent 过滤器之后(在前述 intent 过滤器
一节中已经有了这个示例),它就被设置为一个任务的入口点。这样的过滤器设置会在应用程序加载器中
为此 activity 显示一个图标和标签,以供用户加载任务或加载之后在任意时间回到这个任务。
第二个能力相当重要:用户必须可以离开一个任务,并在一段时间后返回它。出于这个考虑,加载模式被
设定为“singleTask”和“singleInstance”的 activity 总是会初始化一个新任务,这样的 activity 仅能用于
指定了一个 MAIN 和 LAUNCHER 过滤器的情况之下。我们来举例说明如果没指定过滤器的情况下会发生
的事情:一个 intent 加载了一个“singleTask”的 activity,初始化了一个新任务,用户在这个任务中花费
了一些时间来完成工作。然后用户按下了 HOME 键。于是任务被要求转至后台并被主屏幕所掩盖。因为它
并没有在应用程序加载器中显示图标,这将导致用户无法再返回它。
类似的困境也可由 FLAG_ACTIVITY_NEW_TASK 标记引起。如果此标记使一个 activity 启动了一个新任
务继而用户按下了 HOME 键离开了它,则用户必须要有一些方法再次回到这个任务。一些实体(诸如通
知管理器)总是在另外的任务中启动新 activity,而不是做为它们自己的一部分,所以它们总是将
FLAG_ACTIVITY_NEW_TASK 标记包含在 intent 里面并传递给 startActivity()。如果你写了一个能被外
部实体使用这个标记调用的 activity,你必须注意要给用户留一个返回这个被外部实体启动的任务的方法。
当你不想让用户再次返回一个 activity 的情况下,可以将 <activity> 元素的 finishOnTaskLaunch 设

置为“true”。参见前述清理堆栈。


参考文献:android开发指南中文版

0 0
原创粉丝点击