一个应用通常包含多个活动。每个活动应该被设计成围绕某些特定的动作,用户可以执行并且打开其他的活动。举例,邮件应用可能有一个活动展示一系列新消息,当用户选择一个消息,一个新的活动打开去查看这个消息。
一个活动甚至可以打开存在于设备上其他应用的活动。举例来说,如果我们的应用想发送一个邮件,可以定义一个intent去执行发送动作,并且包含一些信息,比如邮件地址和消息。另一个应用的,定义处理这种intent的活动就会打开。在这个例子中 ,inten去发送邮件,所以一个邮件应用的组成活动开始(如果有多个活动支持这个inten,系统会让用户选择,这不就是intent的用法吗,inten还是要看啊)。当邮件发送了,我们的活动重新激活,好像邮件活动就是我们应用的一部分。虽然活动可能来自不同的应用,安卓把这些无缝的用户操作保存在同一个任务中。
一个任务是一系列的活动,用户和他们交互执行特定的一个工作。这些活动被放置在一个栈(返回栈),以每个活动被打开的顺序。
Home屏幕是大多数任务开始的地方。当用户点击一个应用的图标,应用的任务来到最前面。如果应用没有任何任务(应用最近没有被使用),一个新的任务被创建,应用的主活动打开作为栈的根活动(从来没有启动过,重启,启动过的,这个任务在最前面,那么安卓里面岂不是有很多个任务,一个应用就会有一个任务,能不卡吗)。
当当前的活动启动另一个活动,新的活动被推到栈顶,活动焦点。上一个活动保持在栈中,但是停止。当一个活动停止,系统保存他用户界面的当前状态。当用户点击返回按钮,当前的活动被从栈顶推出(销毁),前一个活动resume(他UI的前一个状态被恢复)。栈中的活动绝不会被冲排列,只会推进或者从栈中弹出,被当前活动启动,就推到栈顶,用户通过返回按钮离开,弹出。因此,返回栈表现为一个“后进先出”的数据结构。下下图是demo:
如果用户连续按返回,每一个活动被弹出来展现上一个,直到用户返回主屏幕(或者回到任务开始的执行的地方),当栈中所有的任务被移除,任务就不存在了。
Figure 2. Two tasks: Task B receives user interaction in the foreground, while Task A is in the background, waiting to be resumed.
一个任务是一些紧密结合的单元,可以被移动后台当用户开始一个新的任务或者回到主屏幕(后台运行,这就是安卓的问题,后台跑了太多的东西)。当在后台的时候,栈中所有的任务都停止,但是任务的返回栈保持不变,任务仅仅是失去焦点,另一个代替了他,像图2,。一个任务可以返回到前端,所以用户可以在他们离开的时候返回)。假设,举个例子,当前的任务A栈中有三个活动,两个在当前的活动下面。用户点击home键,然后启动一个新的应用。当新的应用开始,系统为这个应用开始一个新的任务B用他自己的活动栈。在和这个应用交互完之后 ,用户返回主屏幕,选择之前任务A的程序。现在,任务A到最前了,栈中所有的三个活动保持不变,栈中底部的任务resume。这时,用户可以返回到任务B,通过启动程序(或者通过overview
screen选择用户任务)。下图是例子:
Figure 3. A single activity is instantiated multiple times.
注意:后台可以同时保持多个任务。,然而如果用户同时运行了太多了任务,系统可能会开始杀死后台活动来恢复内存,导致活动的状态丢失。
因为返回栈中的活动不会重排序,如果你的应用运行用户可以从多个活动启动一个特殊的活动,会新建一个这个活动的实例推送到栈中(而不是把任何之前的活动实例带到最前)。同样的,我们应用中的一个活动可以实例化多个实例(甚至来自不同的任务,图3所示)。同样,如果用户使用返回键导航返回,每一个活动实例按照他们的打开顺序依次呈现(每一个和他们自己的UI状态)。然而,可以修改这些行为如果不想让他实例化多次,这个会在Managing Tasks中讨论。
总结一下活动和任务的默认行为:
(1) 当活动A开启活动B,活动A亭子,但是系统保存A的状态(比如滚动位置和输入的文本)。如果用户点击返回按钮在B中,A重新激活,状态恢复。
(2) 当用户按Home键离开任务,当前的活动停止,任务进入后台。系统保存任务中的每一个活动的状态。如果用户随后通过选择图标重启开始任务,任务来到最前端,resume栈中最上面的活动。
(3) 如果用户按了返回键,当前的活动从栈中弹出并销毁。栈中上一个活动resume,当一个活动被销毁,系统不会保存活动的状态。
(4) 活动可以被实例化多次,及时是从其他任务。
保存活动状态
上面讨论的,系统默认保存活动的状态当他被停止。这样,用户可以导航返回到之前的活动,他的用户界面和离开他的时候一样。然后,我们可以,并且应该主动使用回调方法保存活动的状态,以防活动被销毁必须重建。
当系统停止了我们的一个活动(必须启动新活动或者任务移到后台),系统可能会完全销毁这个活动如果他需要恢复系统内存。这个发生时,活动的状态信息丢失。如果这个发生了,系统仍然知道活动在返回栈中有一个位置,但是当这个活动重新带到栈的最顶部的时候,系统必须重新创建他(而不是resume)。为了避免丢失用户操作,我们应该主动保存它,通过实现活动的onSaveInstanceState(这个是用户体验很重要的标志啊)
管理任务
上面讨论的安卓管理任务和返回栈的方式,通过把所有连续启动的活动放在同一个任务中,并且在一个“后进先出”的栈中,在大多数的应用中工作很好 ,不用担心活动是怎么和任务关联的或者他们是如何存在返回栈中的。然后,我们可能需要决定想打断这个正常的行为。可能你想要你程序的活动启动一个新任务当他启动的时候(而不是被放进当前的任务中),或者,当你启动一个新活动,你想让返回栈清空所有的活动除了当用户离开任务时的根活动(不理解??)
可以通过mainfes的<activity>属性和传递给startActivity方法的intent里的标记来做这些事。(所以intent还需要好好学习)。
就这一点而言,主要的tivity>中的属性如下:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
主要的inten 标识如下:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
在随后会介绍如何使用manifest属性和intent标识来确定活动如何和任务关联并且如何在返回栈中表现。
当然,在OverviewScreen也是需要考虑的,不过正常情况下,让系统来确定,自己不要改变这个行为。
Also, discussed separately are the considerations for how tasks and activites may be represented and managed in the overview screen. See Overview Screen for more information. Normally you should allow the system to define how your task and activities are represented
in the overview screen, and you don‘t need to modify this behavior.
小心:大多数的应用不应该打断活动和任务的默认行为,如果确定你的活动需要改变默认的行为,要小心使用,认真测试(慎重啊)
Caution: Most applications should not interrupt the default behavior for activities and tasks. If you determine that it‘s necessary for your activity to modify the default behaviors, use caution and be sure to test the usability of the activity during launch
and when navigating back to it from other activities and tasks with the Back button. Be sure to test for navigation behaviors that might conflict with the user‘s expected behavior.
定义启动模式
启动模式允许你定义活动的新的实例如何和当前任务想关联。有两种方式定义不同的启动模式:
? Using the manifest file
When you declare an activity in your manifest file, you can specify how the activity should associate with tasks when it starts.
? Using Intent flags
When you call startActivity(), you can include a flag in the Intent that declares how (or whether) the new activity should associate with the current task.
同样的,如果活动A启动活动B,活动B可以在他的manifest定义他如何与当前的任务关联(如果真的需要),并且,活动A也可以要求活动B应该怎样和当前任务关联。如果两个活动都定义了活动B应该如何和任务关联,活动A的请求(定义在inten中)的比活动B的优先(定义在manif中的)。
注意:一些manifest可用的启动方式在intent中不一定可用,同理,intent中的一些启动方式也不能再manifest中定义。
使用manifest文件
当在manifest文件中定义活动的时候,可以使用<activity>元素的lauchMode属性来指定活动应该如何跟任务关联。
LauchMode属性指定一个指示活动应该如何启动到一个任务中。lauchMode有四种方式可以指定:
"standard" (默认的方式)
默认的。系统会创建一个新的活动实例在启动他的任务,引导intent给他。活动可以多次实例化,每个实例可以属于不同的任务,一个任务可以有多个实例。
"singleTop"
如果一个活动的实例已经存在当前任务的顶部,系统通过intent去找他会调用onNewIntent方法,而不是为活动创建一个新的 实例。活动可以实例化多次,每个实例可以属于不同的任务,一个任务可以有多个实例(但是仅仅当返回栈的顶活动不是这个活动的实例)。
举例来说,假设一个人任务的返回栈包含根活动A,和B,C,D(D在顶部,顺序是A-B-C-D;),如果有一个intent过来请求D,如果D有默认的standard的启动模式,一个新的D的实例被启动,顺序是A-B-C-D-D。如果D的启动模式是singleTop,存在的D的实例通过onNewIntent接收这个intent,因为它在栈的顶部,栈保持A-B-C-D。然而,如果一个intent过来请求活动B,那么一个新的B实例被加入返回栈,即使B的启动模式是singleTop。
注意:当活动的一个新的实例创建,用户可以点击返回按钮返回到上一个活动,但是当一个存在的实例处理一个新的intent,用户不能点返回键返回到活动的状态在新的intent到达onNewIntent之前。(不太理解)
"singleTask"
系统创建一个新的任务,实例化这个活动作为任务的根活动。然而,如果活动的实例已经存在于单独的任务,系统通过调用onNewIntent找到他,而不是创建一个新的实例。只有一个活动的实例存在。
注意:虽然这个活动启动一个新的任务,返回按钮仍然返回上一个活动。
"singleInstance".
和"singleTask"类似,除了系统不会为保存这个活动实例的任务加载其他任何的活动。活动永远是任务唯一的成员,这个活动启动的其他任何活动在新的任务中打开。
作为另一个例子,安卓浏览器应用声明他的web浏览活动应该一直在他自己的任务中打卡。(通过在<activity>中指定为singleTask。这意味着,如果应用issue一个intent打开安卓浏览器,他的活动和你的应用程序不在同一个任务中。相反,无论是为浏览器启动一个新的 任务还是如果浏览器已经在后台有一个运行的任务,这个任务总是会被带到最前面处理这个intent。
无论活动是在一个新的任务中启动还是在启动它的同一个任务中,返回按钮总是可以把用户带到前一个活动。然而,如果启动一个活动指定了singleTask方式,如果一个活动实例存在于后台任务,那么整个任务被带到最前面。这样的话,返回栈包含带到前端的任务的所有活动,在栈的顶端。下图展示了:
.