manifest element一个应用程序通常包含多个Activity。每个Activity都应当被设计成一种特定的功能,用户可以执行它,还可以通过它启动其他的Activity。例如,一个电子邮件的应用程序可能有一个Activity用于展现出新的电子邮件列表,当用户选择了一个电子邮件,就会打开一个新的Activity以查看该电子邮件。
一个活动(Activity)甚至可以启动设备上的另外的一个应用程序的活动。例如,如果您的应用程序想要发送一个电子邮件,您可以定义一个意图(intent)来执行一个“发送”行动,其中包括一些数据,如电子邮件地址和消息。在另一个应用程序,一个Activity声明本身能处理这种意图,然后将自身打开。在这种情况下,intent是为了发送电子邮件,所以电子邮件应用该创建Activity启动器(如果多个活动支持相同的意图,那么系统让用户选择使用哪一个).当发送邮件后,你的Activity将会恢复,这就好像电子邮件的Activity是您的应用程序的一部分一样。即是这些Activity可能来自不同的应用,android系统通过将这些Activity保存在相同的任务栈来维持这种无缝的用户体验。
任务(Task)是活动(Activity)的集合,而活动用于执行用户交互的关键工作。活动以打开的顺序被放入栈中。[注意,这个栈指的就是回退堆栈]
一个设备的HOME屏是大多数任务栈的起点[就是应用程序列表或者桌面]。当用户触摸应用程序图标(或者在桌面上的快捷方式)时,该应用程序的任务就到达前台。如果该应用在任务栈中不存在(应用在近段时间内没有使用过)就会在任务栈中创建一个新的任务,并将该应用的“主”Activity放置在任务栈中作为根activity。
[译者:在开发程序的时候,经常在manifest中配置的↓,可理解为主activity]
action android:name="android.intent.action.MAIN"
当前Activity启动另一个activity时,新的Activity推入堆栈的顶部并获得焦点。之前的Activity仍然在堆栈中,但已停止。当Activity停止时,系统会保留其用户界面的当前状态。当用户按下返回按钮时,当前Activity就会从堆栈的顶部弹出(当前的Activity就会被销毁),同时之前的一个Activity就会恢复(恢复到之前的UI界面)。 Activity在栈中的顺序永远不会改变,只会压入和弹出——被当前Activity启动时压入栈顶,用户用返回键离开时弹出。 这样,back stack 以“后进先出”的方式运行。图1 以时间线表的方式展示了多个Activity在Back stack中放入的顺序和机理。
如果用户继续点击回退按键,那么栈会不断弹出activity并展现之前的那一个,直到HOME屏幕(或者任务开始的第一个activity)。当所有的activity从任务中移除,这个任务也不复存在。
任务是一个聚合的单位,当启用一个新任务或者通过HOME键回到主屏幕,它可被移动到后台。当Task处于后台时,里面所有的Activity都将停止,但是这个Task的回退堆栈(Back Stack)仍然完整保留(原文这里写错了,写成了Back task)在其它Task获得焦点期间,这个Task只是失去焦点而已,如图2所示。TASK可以回到前台,以便用户继续之前的操作。例如:当前Task(Task A)的栈中共有3个Activity,即当前显示的Activity背后有两个Activity。这时,用户按下Home键,然后从应用程序中启动一个新的应用。当 Home 屏幕出现时,Task A 进入后台.当新的应用启动时,系统会为它开启一个Task(Task B),其中放入新应用中的 activity。这个应用完成相应的交互后,用户再次返回 Home 屏幕,并选择了最初启动了 Task A 的应用。现在,Task A 进入前台——栈中的三个Activity仍然完好,位于最顶部的Activity 恢复运行。这时候,用户仍然可以切回 Task B,通过回到 Home 屏幕并选择相应图标即可(或者触摸并按住Home键调出最近Task列表并选中)。这便是 Android 多 task 的实例。
注意:多任务可以被放入后台。然而,一旦用户在同一时刻运行了太多的后台任务,系统可能会执行销毁后台activity来回收内存,导致activity的状态丢失。看章节Activity state.
因为回退栈中的后动永远不会被重排,如果你的应用程序允许用户多次启动一个activity,一个新的activity实例将会创建并放置到栈的顶部(而不是把之前那个移动到顶部)。这样的话,如果用户使用 Back按键,每个活动的实例将会以被打开的顺序展示。然而,如果你不希望一个实例被打开多次,你可以修改这些行为。如何做,请看章节Managing Tasks.
活动和任务的默认行为总结:
Navigation Design
For more about how app navigation works on Android, read Android Design's Navigation guide.
如前所述,当Activity停止时,其系统默认的行为会保存状态。这就意味着当用户通过导航返回之前的活动时,它看起来和离开时没什么两样。然而,你能够——并且应该积极地通过回调函数来保存它的状态,倘若活动被销毁并被重新创建。
当系统停止你的一个活动(例如启动了一个新的活动,或者活动移动到后台),系统也许会销毁你的活动,并回收其占用的系统内存。当这发生时,你的活动状态的信息将会丢失。倘若这发生了,系统仍然知道这个活动在回退栈上占据了一个位置,当活动被带到回退堆栈的顶部时,系统必须重新创造它recreate(而不是恢复它resume)。为了避免丢失用户的工作,你应该积极的保存它,通过实现你的活动中的onSaveInstanceState()回调方法。
请参阅Activities文档。
安卓管理任务和回退栈的方式,已经在前文进行了描述——通过“后入先出”的方式将活动连续的放入回退栈中——它在绝大多数应用程序中运行良好,你不必关心你的活动如何和你的任务结合及活动怎样存在于回退栈中。然而,你也许决定终止正常的行为。或许你想要你的应用程序以新任务开启你的活动,或者,当你开启活动时,你想要使用之前的已存在的活动实例(而不是重新在回退栈的顶部创建一个),或者,你想要当用户离开任务时,回退栈清除除了根活动外所有的活动。
你可以做的更多,使用 位于配置文件中的
标签元素,以及设置传递到startActivity()方法中的intent的标识位。
中主要可使用的属性包含:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
intent标记中可使用的:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
在后面的章节,你将会看到如何使用它们,及它们将会怎样作用于你的活动及回退栈的行为。
注意:大部分应用程序不应当改变默认的活动与任务的行为。如果你确定有必要修改默认行为,谨慎的使用,你需要测试可用性——自桌面启用,从其他活动导航到指定活动,任务中回退到达。确保这些测试被执行,以免与用户的行为预期发生冲突。
启动模式允许你定义新的活动的实例与当前任务的结合关系。你可以使用两中方式来定义:
当你在配置文件中声明你的活动,你能够指定活动启动时与其集合的任务之间的关系。
当你调用startActivity(),你可以使用一个含有标记的 Intent。它可以声明启动活动的方式。
举个例子,如果A启动B,那么B可以在配置文件中定义它应该如何与当前任务相结合,A也能够请求B该如何启动。如果两个活动都定义了B应该如何结合任务,那么A的请求(定义在intent中)应该遵从B的请求(定义在配置文件中)。
注意:一些启动模式在配置文件中有定义,但是在intent 标记中没有,反之亦然。
在配置文件中,使用
元素中的launchMode
属性来定义你的活动如何结合当前任务。
启动模式(launchMode
)属性可被指定下面这些值:
"standard"
(the default mode)
标准(默认模式)
"singleTop" 单一顶部
举个例子,假设任务的回退栈中有A,B,C,D四个活动,其中A是根活动,D在顶部。如果D使用默认的启动模式,则向上面添加一个新的D的实例,回退栈变为A-B-C-D-D。然而,如果是“singleTop”,那么Intent中如果是D,将会执行已存在的D将被调用回调函数 ,而不会创建新的实例,所以整个回退栈中将是A-B-C-D。如果是B,则会添加一个新的实例在D上面,即使启动模式是“singleTop”。
注意:当新的实例被创建时,用户点击BACK将回到之前的活动。但当一个活动实例处理新的意图时,在活动进入方法onNewIntent()前,用户不能按回退按键回到之前的状态。
"singleTask" 单一任务
注意:尽管活动可以启用新任务,但是回退按键仍然可以回到用户之前的活动。
"singleInstance" 单一实例
作为另一个样例,安卓浏览器应用程序声明网络浏览器活动应该以被自身拥有的任务打开——通过在配置文件(manifest.xml)中指定
无论活动在一个新的任务中启动,还是与启动这个活动相同的活动中启动,回退按键总是回到用户之前的那个活动中,然而,如果活动以指定的【单一任务】模式启动,如果启动的活动存在于一个处于后台的任务中,那么整个任务都将回到前台。在这一点上,回退栈包含所有的活动都将带回前台,并置于回退栈的顶部。图4阐述了这种情节。
请查阅manifest文件,察看
注意:启动模式属性能够被启动活动的意图所包含的标记重写,下个章节我们讨论这个问题
当启动一个活动,你可以通过设定标记修改默认的活动结合性,这些标记将传递给startActivity()。这些标记用户修改默认的行为:
FLAG_ACTIVITY_NEW_TASK 标记—活动-新任务
在新的任务中开启一个活动。如果任务已经运行了你准备启动的活动,那么任务只会将其带到前台,并使得活动通过 onNewIntent()
接收这个新的意图。
这就相当于在启动模式中设定值为"singleTask"。
FLAG_ACTIVITY_SINGLE_TOP 标记-活动-单一顶部
如果被启动的活动是当前活动(处于回退堆栈的顶部),那么已存在的实例接收一个 onNewIntent()
的调用,而不是创建一个新的活动的实例。
这与之前讨论的启动模式值为"singleTop"的情形一样.
FLAG_ACTIVITY_CLEAR_TOP
如果活动已经运行在当前的任务中,那么并不会新建一个活动的实例,而是要启动的摧毁所有其顶部的活动,然后通过onNewIntent()恢复这个实例(现在它处于顶部了)。
启动模式没有对应的值。
经常与FLAG_ACTIVITY_NEW_TASK结合使用。当一同使用时,这些标记定位一个在其他任务中已存在的活动,并放置到其可以接收intent的位置。
注意:如果活动的启动模式被设计为“标准的”,它将不会移除栈中的活动,且新的实例将要处理接收的intent。因为当模式是“标准的”时,新的实例总是要在新的intent中创建。
结合性是用于标识活动偏好的任务。默认情况下,来自同一应用程序的所有的活动与其他活动具有相同的结合性。也就是说,默认情况下,同一应用程序的所有的活动处于同一任务中。然而,你可以修改活动的默认结合性。定义在不同应用程序中的活动可以分享一个结合性,或者定义在同一应用程序的活动可以被指派不同的任务结合性。
你可以通过
元素的taskAffinity
属性给任何活动赋予结合性。
两种情况下结合性可用:
默认情况下一个新的活动,通过调用startActivity()启动自任务中的活动。它将被启动的活动压入启动者的任务中。然而,如果传递startActivity()的意图中包含FLAG_ACTIVITY_NEW_TASK标记,系统将会为这个活动寻找一个新的任务作为它的新家。然而通常情况下,这是没有必要的。
如果标记导致启动了一个新的任务,并且用户通过按HOME键返回,这就必须有让用户导航返回到任务的方法了。一些实体(例如通知管理)总是在外部任务中启动活动,而从不在自己的空间中,所以它们在intent中设置标记FLAG_ACTIVITY_NEW_TASK
。如果你拥有一个能够被可能拥有此标记的外部实体调起的活动,注意用户可能有独立的方法回到任务启动的地方,例如启动器的图标。
allowTaskReparenting
属性设定为“true” 在这种情况下,当任务到达前台时,活动能够从之前附属的任务中移动出来
举个例子,假设一个报告所选城市天气状况的活动,它作为旅行应用程序的一部分。它与其他的活动处于同一应用程序中,具有相同的结合性(默认应用程序结合性),它加上这个标记允许“更换父母”。当你的一个活动开启这个天气报告活动,它最初属于启动的那个任务。然而,当旅行的应用程序任务来到前台后,这个天气报告活动将重新分配到旅行的那个任务中并展示出来。
提示:如果.apk文件包含不止一个用户视图点,你可能需要这个属性。
如果用户长时间离开一个任务,系统清除除了根活动以外的任务。当用户再次返回到任务时,只有根活动被保存了。系统之所以是这样的行为,是因为用户倾向放弃那些他们之前离开执行新任务后返回后的任务。
这里有一些活动属性可以修改这个行为:
alwaysRetainTaskState 总是保存任务状态
clearTaskOnLaunch 清除任务到启动
alwaysRetainTaskState
是刚好相反的。用户总是返回任务原始的状态,即使只离开了一小会儿。
finishOnTaskLaunch 完结自任务启动
clearTaskOnLaunch
类似,但仅仅是操作单个活动,而不是整个任务。它也可能触发活动离开,包含根活动。当它被设定为“true”,活动仅仅在当前阶段得以保存。如果用户离开,再返回,它就不再存在。
通过设置意图过滤器,以"android.intent.action.MAIN"为活动,以"android.intent.category.LAUNCHER"为类别;你可以启动一个活动作为一个任务的入口。举例如下:
... >
... >
android:name="android.intent.action.MAIN" />
android:name="android.intent.category.LAUNCHER" />
...
这个的过滤规则导致会在应用程序“启动器”上面显示一个图标,给用户一个启动活动或者回到任务的方法。
第二能力是非常重要的。用户必须有能力离开任务,然后使用启动器返回它。因为这个原因,两种启动模式"singleTask"和"singleInstance"应该用于当活动过滤器设定为ACTION_MAIN和CATEGORY_LAUNCHER。想象一下,如果过滤器丢失了会怎么样:一个意图启动了一个 活动,开启了一个新任务,用户花费了大量的时间在这个任务上。用户点击了HOME按钮。任务被放入后台,且不可见。用户也没有办法返回这个任务,因为它不再启动器上呈现。