Activities 和任务(Tasks)

    一个activity可以启动另外一个包含它其他程序中的activity。想象一下,例如,你想要用户显示一个街道的地图,已经有另外一个程序可以做这件事情,所以你的程序只需要建立一个intent对象,这个intent对象包含请求的数据,并调用startActivity().这时候,地图组件可以显示地图,当用户点击“后退”按钮,你的activity会重新显示在屏幕上。

    对用户来说,即使地图程序是定义在另外一个程序中、在另外一个进程中运行,但是它看起来好像你的程序的一部分。Android通过将2个activities放在用一个任务中,使得用户有这样的体验。这一系列的activities,安排在一个堆栈中。堆栈中的根activity是任务的开始,或者说是用户选择的应用程序的第一个activity。堆栈顶部的activity 是当前正在运行的activity— 目前正在吸引用户注意的一个.当一个activity启动另外一个的时候,新的activity被压栈,变为当前activity. 之前的activity 还在任务中。当用户点击后退(BACK)键,当前的acitivity被弹出栈,之前的activity成为正在运行的。

    如果堆栈中有多个同样的activity的实例,如有多个地图查看器(如程序设定了多个打开地图查看器的入口),那么堆栈不可以重新排序,只能弹出和压入。

    任务是activities的堆栈,而非装箱单中的一个类,因此不能离开activity单独设定一个堆栈的值。例如,下面一章将要谈到‘任务的吸引力’,这个值就是设定在任务的根activity里面的。

    任务中的所有activities作为一个整体移动。整个任务可以放在前台(foreground)或者后台(background)运行。想象一下,例如,有个任务有4个activities,用户点击“HOME”键,回到程序开始的地方,选择了一个新的程序(其实是一个新的任务),当前的任务移到后台运行,新任务的根activity开始显示。过了一会,用户又回到HOME界面,选择了刚才的程序(刚才的任务),则这个含有4个activities的任务回到前台运行。当用户点击BACK按钮,屏幕不会显示用户刚刚离开时候的activity,而是在堆栈顶部的activity被弹出,显示前一个activity。

    默认的情况下,上面这些描述是没有问题的。当然,也有很多办法来改变以下:任务和activity的关系、任务中一个activity的动作。这些是被Intent对象(启动此acivity的)的标志集(flag set)和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

下面的章节说明了这些标记和属性的意义和互动、需要注意的问题:

Affinities 和新任务

每个acitivity有一个默认的affinity。然而,特定affinity可以通过<activity>的taskAffinity属性被设置到特定的activity的上。不同程序的activities可以共享一个affinity;同一个程序的activity可以使用不同的afninity。afninity在符合以下2个条件的时候启动:当启动acitity的Intent中含有FLAG_ACTIVITY_NEW_TASK 节点,或者一个activity的allowTaskReparenting 属性设置为"true".

FLAG_ACTIVITY_NEW_TASK 节点

之前提到,任务的默认通过startActivity(). 激活的默认activity也和其他activity在一个堆栈里面。但是引发这个activity的Intent通过startActivity() 方法传递了 FLAG_ACTIVITY_NEW_TASK 节点。望文知意,这个节点表示新的任务。但是,也不一定;如果任务中没有含有此afinity的activity,那么它从默认的activity开始。

allowTaskReparenting 节点

如果activity的 allowTaskReparenting 属性设置为 "true", 它就可以脱离开始它的任务,然后通过它的afinity指引,走入下一个任务。例如,假设“旅游软件”有一个activity可以获取指定城市的天气。这个activity和此程序中的其他activity有同样的afinity(默认的afinity),并且允许重新转入另外的任务。某个activity开始了天气预报动作,它一开始属于这个任务;但是旅游程序到前台运行的时候,天气预报acivity被分配到旅游程序运行。

如果从用户角度看,一个.apk文件包含许多程序,你必须适当的分配不同的affinity给activitis。

启动模式

一个 <activity> 有4种启动模式,要设置 launchMode 属性:

"standard" (the default mode)
"singleTop"
"singleTask"
"singleInstance"

这些模式有以下区别:

哪个任务将包含intent指定要运行的activity:对于 "standard" 和 "singleTop" 模式,启动activity的intent (即调用 startActivity()方法的) —除非intent对象包含FLAG_ACTIVITY_NEW_TASK 节点,如果是的话,会像上个章节描述的一样, Affinities and new tasks,另外一个任务会承载这个activity。

相对的, "singleTask" 和 "singleInstance" 模式使得activity永远在tast的根,他们初始化一个任务,不会融入到其他任务中 。

activity是否会有多个实例. "standard" 或 "singleTop"模式的activity可以被示例化多次,属于多个任务,或者一个任务中有多个此activity的实例。

相对的,, "singleTask" 和 "singleInstance" 模式的activities限制为只能有一个实例。因为这些activity在任务的根,这表示,任务也只有一个。

同一个任务中是否可以有其他的activity: "singleInstance" 模式的activity所在的任务中不能有其他的activity ,如果启动其他activity, 那么其他activity 会被附加到新的任务(不管新activity的启动模式是什么) — 就像intent使用了 FLAG_ACTIVITY_NEW_TASK 标记。在其他方面, "singleInstance" 和 "singleTask"没有什么区别。

其他三种模式允许多个acitvity在同一个任务中。singleTask 的activity必须在任务的根,它可以启动其他设置到该任务的activity。而standard" 和 "singleTop"可以用于任务的任何位置。

一个类的新实例是否可以接受、处理其他部分发来的intent: 默认的"standard" 模式可以响应任何新的intent,每个实例处理一个intent。 "singleTop" 模式的activity如果在任务的顶部,则用已有的实例处理所有请求该实例的intent;反之创建一个新的实例,并压入任务的顶部。

例如,一个任务包含根activityA,和其他activityB,C,D,D在任务堆栈的顶部,即堆栈为A-B-C-D. 一个intent请求D,如果D是"standard" 启动模式,则D的一个新实例被创建,任务堆栈变为A-B-C-D-D. 但是,如果D的启动模式是"singleTop", 则现有的D的实例来响应intent,所以任务堆栈仍然是A-B-C-D.

另外一种情况,如果intent来请求B,如果B是 "standard" 或者 "singleTop" 模式,B都将创建一个新的实例来响应,则任务堆栈为 A-B-C-D-B.

就像上面提到的,"singleTask" 或 "singleInstance" 的activity只有1个实例,所以这个实例要处理所有的intent。"singleInstance" 的activity永远在任务堆栈的顶部(因为它是任务中的唯一个activity),所以这个实例处理所有的intent。可能有其他的activity在"singleTask" 的activity的顶部,因此这个实例不会处理intent请求,因此intent的请求会失败;(即使失败,intent也会激活这个程序)

当已存在的acitity用来处理intent的时候,intent会引发activity的onNewIntent() 方法. (activiy可以调用getIntent()获取触发它的intent)

注意:当一个新的activity的实例为了响应intent而创建时,用户可以按BACK键回到上一个状态(上一个activity)。但是当一个已经存在的activity处理新的intent的时候,用户不能按BACK键后退了。

请参见 The AndroidManifest.xml

清空任务

如果一个用户长时间没有使用一个任务,系统会请空这个任务中根以外的acitivity。当用户再次使用此程序的时候,只有最开始的activity显示出来。这样做的原因是:当用户很长时间以后回来的时候,它倾向于重新开始工作,而不是接着上次的工作做。

当然,这是默认的模式。activity有一些属性可以控制请空的动作。

alwaysRetainTaskState属性

当任务的根acitvity的此属性设置为 "true" 的时候,以上描述的动作不会执行。系统会保留上次程序加载时候的所有acitivity。

clearTaskOnLaunch属性

当任务的根acitvity的此属性设置为 "true" 的时候,无论用户何时返回应用程序,程序都会请空队列。从另一个角度说,这个属性和alwaysRetainTaskState属性完全相反。即使离开一会,用户也会被导航到初始状态。

finishOnTaskLaunch属性

这个属性有些像clearTaskOnLaunch, 但是它是针对某个activity而非整个任务的。它可以清除任何activity ,当然包括任务堆栈的根activity。当此属性设置为"true" 的时候,此activity只在用户当前会话中显示,当用户离开再回来的时候,此activity已经被清除。

有另外一种方式来请空任务。如果intent含有 FLAG_ACTIVITY_CLEAR_TOP 节点,并且目标任务已经有一个指定activity的实例来处理这个intent了,那么这个activity顶上的节点将被全部清除,这个activity的实例就位于任务堆栈的顶部了。如果指定的activity设置为"standard", 它将从任务中移除,建立一个新的实例来响应此intent。因为启动模式设置为 "standard"的时候,会建立一个新的实例来响应intent。

FLAG_ACTIVITY_CLEAR_TOP 经常和FLAG_ACTIVITY_NEW_TASK.一起使用。当他们一起使用的时候,可以定位到另外一个任务中已经存在的activity上,使得这个acitivity响应intent。

开始任务Starting tasks

怎样把一个activity设置为整个任务的入口呢?可以设置一个intent filter: "android.intent.action.MAIN" 作为默认动作;"android.intent.category.LAUNCHER" 作为类别。 (是 Intent Filters 章节的例子)。这种类型的的filter对应的activity的icon和label显示在任务中,这样用户可以方便的回到任务

第二个能力尤其重要,用户必须可以离开任务,并且重返任务。因此, "singleTask" 和 "singleInstance"这2个启动模式必须用在含有 MAIN 和 LAUNCHER filter的activity中.想象一下,如果不加这2个filter会怎样?一个intent请求"singleTask" 的activity,初始化了一个新的任务,用户操作了一会这个任务。然后用户点击“HOME”键。现在这个任务就被HOME窗体覆盖了。但是因为这个任务不能在application launcher(注:类似于进程管理器)里面看到,因此用户没有办法打开它。

FLAG_ACTIVITY_NEW_TASK 标志也有同样的问题。这个标志将使得activity在一个新的任务执行,当用户点击HOME 键离开任务的时候,必须能让用户回到这个任务。其他的一些机制(如通知管理器)经常在外部的任务开启某个activity,而不是activity所在的任务本身,因此必须在intent触发 startActivity()方法时使用FLAG_ACTIVITY_NEW_TASK 标记。如果你有一个可以使用这个标记并且可以被外部调用的activity,要注意使得用户可以重新回到这个任务。

当你不想用户重新回到任务的时候,将 <activity> 节点的finishOnTaskLaunch 设置为"true". 请参见前面的 Clearing the stack。

posted @ 2010-10-20 18:24  IT圈儿  阅读(777)  评论(0)    收藏  举报