Android官方开发指南-Tasks and Back Stack

来源:互联网 发布:手机的mac地址可以改吗 编辑:程序博客网 时间:2024/05/06 16:19

任务和栈

一个应用程序通常包含多个activities。每一个activities都应该被设计成围绕一个特定的动作,这些动作用户可以执行,可以启动其他activity。例如,电子邮件应用程序可能有一个activity,以显示新的消息的列表。当用户选择一个信息,一个新的activity界面打开,以查看该消息。

一个activity甚至可以启动存在于设备上的其他应用程序的activities。例如,如果你的应用程序想要发送电子邮件,你可以定义一个意图进行“send”动作,并包含一些数据,如电子邮件地址和消息。其他应用程序的activity声明它自己去处理这类Intent,然后打开。在这种情况下,目的是发送电子邮件,因此电子邮件应用程序的“撰写”activity启动(如果有多个activity支持相同的意图,系统将允许用户选择使用哪一个)。当电子邮件被发送,你的activity恢复,并且电子邮件activity好像是应用程序的一部分。即使activity可以是来自不同的应用程序,Android系统也会通过相同的task来保存这种用户体验。

任务是当用户执行某项工作时与用户交互的activity的集合。activity被安排在返回栈中(the back stack),在其中,每个activity按顺序启动。

设备的主屏幕是大多数task的起点。当用户触摸应用程序启动器上的图标(或主屏幕上​​的快捷方式),该应用程序的task就会转到前台。如果没有任务存在该应用程序(该应用程序最近尚未使用),则创建一个新的task,此应用程序的“main”activity启动,作为栈中的根activity。

当明确的activity启动另一个activity时,并新的activity被放到activity栈的最上面,并获得焦点。之前的activity保持在栈中,但是被停止。当activity停止时,系统保存其与用户交互的当前状态。当用户按下 返回 按钮时,当前activity,从栈的顶部弹出(activity被销毁),前一个activity恢复(其UI的先前状态被恢复)。activity在栈中的位置从不重新排列,只有入栈和出栈—当一个明确的activity启动时,入栈,当用户按下返回按钮返回时,出栈。因此,栈作为一个“后进先出”的对象结构。图1用时间轴表示沿与当前背叠在每个时间点的activity之间的进度来可视化此行为。

图1,在一个任务中的每个新的activity如何增加了一个项目到栈中的表示。当用户按下返回按钮时,当前activity被销毁,之前的activity恢复。

如果用户继续按返回,然后在堆栈中每个activity被弹出,显示前一个,直到用户返回到主屏幕(或任何activity正在运行时,在任务开始时)。当所有activity都从栈中移除,task不再存在。

图2。两个任务:taskB接收在前台的用户交互,而任务A是在后台,等待被恢复。

图3。一个activity被实例化多次。

一个task是一个有机的整体,可以移动到“后台”,当用户开始一个新的task或通过主页按钮进入主屏幕。而在后台,在task中的所有activity都时停止的,但task的栈保存完整—当task被另一个task取代时,前task仅仅是失去焦点,如图2。一个task可以返回到“前台“,以便用户继续之前的操作。例如,假设当前的task(taskA)在它的栈中有三个任务,其中两个处于当前的activity之下。用户按下主页 按钮,然后开始从应用程序启动器(Launcher)中启动一个新的应用程序。当主屏幕出现时,taskA进入后台。当新的应用程序启动时,系统为这个应用程序启动一个task(taskB),该应用程序拥有自己的activity栈。与该应用程序交互之后,用户再次返回主页,并选择最初启动的任务A。现在,task A出现在“前台”-所有该task中的三个activity还是原样,并且栈顶的activity恢复。在这一点上,用户还可以通过进入Home,并选择开始该任务的应用程序图标(或通过触摸并按住HOME键,显示最近的task,并选择其中的一个)。这是一个在Android上的多task的例子。

注:后台可以同时保持多个任务。然而,如果用户在同一时间运行多个后台task,系统可能会开始销毁后台activity,以回收内存,从而导致activity状态丢失。请参阅有关下一节Activity state。

因为在栈中的activity永远不会重新排列,如果你的应用程序允许用户从多个activity启动某个特定的activity,该activity的一个新的实例被创建,并被压入栈(而不是把该activity之前的任何一个实例带入到栈顶)。这样,在应用程序中一个activity可能被多次实例化(甚至从不同的任务),如图3所示。如果用户用BACK键导航返回时,activity的每个实例都会按照原来打开的顺序显示出来(每个activity都有自己的UI状态)。但是,您可以修改此行为,如果你不想一个activity被实例化多次。如何这样做,在后面的关于Managing Tasks的章节中讨论。

总结Activity和task的默认行为:

  • 当Activity A开始Activity B,Activity A被停止,但系统保存其状态(如滚动位置和输入到表单的文本)。如果在Activity B中用户按下后退按钮,Activity A恢复其之前保存的状态。
  • 当用户通过按Home按钮离开一个task时,当前Activity停止,并且它的task进入后台。系统保存在task中的每一个活动的状态。如果用户通过选择开始该task的启动图标来恢复它的task,这个task转到前台,并且恢复该Activity到栈的顶部。
  • 如果用户按下后退按钮时,当前的Activity从栈中弹出并销毁。在栈中的前一个Activity恢复。当Activity销毁,系统不保留Activity的状态。
  • Activity可以被实例化多次,甚至从其他task。

导航设计

欲了解更多有关应用程序的导航如何在Android上工作的,请阅读Navigation 指南。

保存Activity状态


如上所讨论的,系统默认在一个Activity被停止时保存一个它的状态。这样,当用户用导航返回到之前的Activity时,它的用户界面看起来跟它们失去焦点(转到后台)之前一样。不过,你可以-并且应该 -用回调方法主动保存Activity的状态,以避免Activity被销毁,必须重新创建。

当系统停止你的Activity中的一个时(当一 ​​个新的活动开始或task转到后台,如),如果系统需要恢复内存系统可能会完全销毁该Activity。当这种情况发生时,关于该Activity状态的信息会丢失。如果发生这种情况,则系统仍然知道该Activity在栈中,但是,要将该Activity带到栈顶,系统必须重新创建它(而不是恢复它)。为了避免丢失用户的操作内容,你应该主动保存这些内容,通过实现Activity的onSaveInstanceState() 的回调方法

有关如何保存你的Activity状态的详细信息,请参阅Activities 文档。

管理Tasks


 如上所诉,把所有已启动的activity相继放到相同的task中和“后进先出”的栈中,Android 管理task和栈的这种方式适合大多数应用。你不用担心关于你的activity是如何与task关联的,以及如何出栈的。不过,有时你也许想改变这种普通的运行方式。也许你想你应用中的一个activity启动的时候创建一个新的task(而不是存在于当前的task中)。或者,当你启动一个activity时,你想调出一个现有的实例(而不是在栈顶创建一个新的实例)。或者,当用户离开这个task时,你想栈只保留根activity,而栈中的其他activity都清空。

你能做的事还有很多,利用 <activity>manifest 元素的属性,以及你传给startActivity()的Intent中的flags(标志)。

在这方面,主要的<activity>属性,你可以使用如下:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

主要的意图标志,你可以使用如下:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

在下面的章节中,你将看到如何使用这些manifest属性和意图标志来定义activity与task是如何关联的,以及它们在栈中是如何运作的。

此外,单独讨论的是关于在overview 屏(总览屏幕)中activitiy和task可能如何表现和管理的考虑。更多信息请参考Overview Screen。通常情况下,你应该让系统来定义你的task和activity总览屏幕如何体现的,你也不需要修改此行为。

注意:大多数应用程序不应该干扰的activity和task的默认行为。如果确定有必要为你的activity改变这种默认行为,请谨慎使用,并确保启动期间测试activity的可用性,以及通过BACK按钮从其他activity和task返回到这个activity时的可用性。请确保可能对与用户预期的导航方式相冲突的地方进行测试。

定义启动模式

启动模式可以让你定义activity的一个新实例与当前task的交互方式。你可以通过两种方式来定义不同的启动模式:

  • 使用清单文件(Using the manifest file)

    当你在manifest中声明一个activity时,你可以指定activity启动时如何与task交互。

  • 使用意向的标志(Using Intent flags)

    当你调用startActivity()时,可以在Intent中包含一个标志,用来声明新的activity如何(是否)与当前的task相关联。

因此,如果activity A启动activity B,activity B可以在其manifest中定义应该如何与当前task相关联(如果有的话),并且activity A也可以要求activity B应如何与当前task相关联。如果这两个activity都定义了activity B应如何与task相关联,那么activity A的请求(如定义在Intent中)将会超过 activity B的请求(如在manifest中定义)。

注:适用于manifest文件中的一些启动模式不适合在Intent标志中使用,反之亦然,Intent中某些启动模式的标志并不适合在manifest中定义。

使用清单文件

在manifest文件中声明一个activity时,你可以利用 <activity> 元素的launchMode属性来这定activity与task之间的关系。

launchMode属性指定activity启动task的方式。 launchMode 属性可以被设置为四种不同的启动模式

“standard”(默认方式)
默认值。系统在task中创建activity的一个实例,并把Intent的路径给它,activity可能被实例化多次,而每个实例又可能属于不同的task,并且一个task可能有多个实例。
“singleTop”
如果activity的一个实例已存在于当前task的顶部,系统调用已有实例onNewIntent()方法把intent传递给已有实例,而不是创建activity的新实例。activity可以被实例化多次,每个实例可以属于不同的task,一个任务可以有多个实例(但只有栈顶的activity不是activity现有实例)。

例如,假设一个task的栈包含根activity A 以及activity B,C和D。(栈是A-B-C-D; D是栈顶)。一个关于类型D的Activity的Intent抵达。如果D有默认的“standard”启动模式,该类的一个新实例被启动,栈变得ABCDD。然而,如果D的启动模式是“singleTop”,D的现有实例通过onNewIntent()接收到Intent因为它在栈顶—栈仍保持A-B-C-D的状态。然而,如果关于B类型的activity的意图到达,则B的一个新实例会被添加到栈中,即使它的启动模式是“singleTop” 

注意:当一个Activity的新实例被创建时,用户可以按下后退按钮以返回到前一个Activity。但是,当Activity的现有实例处理新的意图,用户不能按后退按钮返回到onNewIntent()中Intent到来之前的activity状态。

“singleTask”
系统会创建一个新的task和实例化新task中的根activity。不过,如果activity的实例已存在于一个单独的task中,那么系统通过调用其现有实例onNewIntent()方法把Intent传给现有实例,而不是创建一个新实例。此activity在同一时刻只有一个实例存在。

注意:虽然在activity在一个新的task中启动,但是后退按钮仍然使用户返回到先前的activity。

“singleInstance” 
相同于“singleTask” ,所不同的是,系统不启动任何其他activity到现有的task中。activity始终是它所在task中唯一的成员; 它所启动的任何activity都会被放入到其他task中。

另一个例子,Android浏览器应用程序声明Web浏览器的activity应该总是在它自己的task开启—通过在 <activity>元素中指定singleTask启动模式。这意味着,如果你的应用程序发出打开Android浏览器的intent,它的activity是不会放在与你的应用程序所在的同一个task中。相反,无论是在新的task中启动浏览器,或者,如果浏览器已有task在后台运行,那么这个任务被转到前台来处理新的intent。

不管是在一个新的task中启动activity,还是在activity启动时已有的task中,用户总是可以用返回键返回到前一个activity中。但是,如果你启动一个activity,它已指定了singleTask启动模式,并且在后台task中存在该activity的一个实例,那么后台的task就会完全转到前台。这样的话,目前栈中包含的来自于转到前台的task的所有activity处在栈顶。图4显示了这种类型的场景。

图4。启动模式为“singleTask”的activity如何入栈的体现。如果activity已经存在于后台task中,并带有自己的栈,则整个栈都会被转入前台,并放入到当前的task顶部.

关于在manifest中使用启动模式的更多信息,请查阅 <activity> 元素文档,在此文档中,launchMode属性和可用值讨论的更多。

注意:你用launchMode属性为activity设置的启动模式可以被启动的activity的intent标志所覆盖,这将在下一章节中讨论。

使用Intent标志

当启动一个activity时,你可以通过在Intent中包含flags并将此Intent传递给 startActivity()的方式来改变你activity与task的默认关联方式。这些flags你能用来修改默认的方式:

FLAG_ACTIVITY_NEW_TASK
在一个新task中启动activity。如果task为activity已经运行,那么这个task会被转到前台,并保持其最后所保存的状态,并且activity在onNewIntent()中将接收新的intent。

这将与在上一节中讨论的"singleTask" launchMode 启动模式值产生相同的过程。

FLAG_ACTIVITY_SINGLE_TOP
如果被启动的activity是当前activity(在栈顶),则现有的实例接收一个消息去调用onNewIntent(),而不是创建activity的新实例。

这将与在上一节中讨论的“singleTop” launchMode 启动模式值产生相同的过程。

FLAG_ACTIVITY_CLEAR_TOP
如果被启动的activity在当前task中已经运行,则不再创建此activity的一个新实例,所有在此activity上面的其他activity被会销毁,并且此意图通过onNewIntent()将被传递给被恢复activity的实例(当前在在栈顶)。

此种模式在launchMode属性中没有与其对应的值。

FLAG_ACTIVITY_CLEAR_TOP经常与FLAG_ACTIVITY_NEW_TASK配合使用。当一起使用时,这些标志是的一种方式,用来定位存在于另一task中现有活动,并把它放在一个位置,在这个位置上它可以响应Intent。

注:如果被指定的activity的启动模式为 “standard”,它也会被弹出栈,同时一个新的实例取代它的位置并处理即将到来的intent。这是因为,当启动模式为standard时,为了一个新的Intent,新的实例总是要被创建。

处理affinities

该affinities指示activity更偏向于那个task。缺省情况下,来自于同一个应用程序的所有activity都对彼此具有亲和性(affinities)。所以,在默认情况下,在相同的应用程序的所有activity倾向于在相同的task中。但是,您可以改变activity的这种默认亲和力。定义在不同的应用程序的activity可以共享亲和力(affinities),或者相同应用程序中的activity也可以赋予不同的task affinities。

您可以为任何已给定的activity修改affinities,此activity有 <activity> 元素中的taskAffinity属性

taskAffinity 属性需要一个字符串值,必须与<manifest> 元素定义的包名保证唯一,因为系统把这个包名用于标志应用的默认task affinity 值。

affinity 在下面两种情况下发挥作用:

  • 当启动一个activity的意图包含 FLAG_ACTIVITY_NEW_TASK 标志。

    默认情况下,一个新的activity 将被放入调用 startActivity() 的activity 所在 task中,作为调用者它被压入栈。 不过,如果传给 startActivity() 的 intent 包含了 FLAG_ACTIVITY_NEW_TASK 标志,则系统会查找另一个 task 并将新 activity 放入其中。通常,它是一个新的task,但是这也不是必要的。 如果一个已有 的task 的affinity值与新activity 的相同,则 activity 会放入该 task中。 如果没有,则会新建一个新 task。

    如果此标志使activity创建了一个新的task,而用户要按下Home键离开此界面时,必须采取一些措施让用户能回到此task。 某些应用(比如通知管理器)总是在其他task中启动activity,从不是放入自己的 task中。 因此,它们总是把 FLAG_ACTIVITY_NEW_TASK 标志置入传给 startActivity() 的intent 中。如果你的activity可以被外部应用携带此标志来启动,请注意用户会用其它方式返回启动 task,比如通过应用图标(task 的根activity 带有一个CATEGORY_LAUNCHER intent 过滤器;参阅下节Starting a task)。


  • 当一个activity都有其自己的allowTaskReparenting属性,并设置为“true”。 

    这种情况下,当某个 task 进入前台时,activity 的 affinity 值又与其相同,则它可以从启动时的 task 移入这个 task 中。

    比如,假定某旅游应用中有一个 activity 根据所选的城市来报告天气情况。 它的 affinity 与同一应用中的其它 activity 一样(整个应用默认的 affinity),且它允许重新指定此属性的归属。 当你的另一 activity 启动此天气报告 activity 时,它会在同一个 task 中启动。 然而,当旅游应用的 task 进入前台时,则天气报告 activity 将会重新放入其 task 中并显示出来。

提示:从用户的角度出发,如果一个.apk文件包含多个“应用程序”,你可能需要用 taskAffinity 属性来指定每个“application”中 activity 的 affinity 值。

清理栈(Clearing the back stack)

如果用户离开一个task很久了,系统会清除除了根activity之外的所有activity的task。当用户再次返回到此task,只有根activity被恢复。系统的这种执行方式,因为经过很多一段时间以后,用户很可能已经放弃了之前他们做过什么,返回task开始新的东西。

有一些activity属性,你可以用它来修改此行为:

alwaysRetainTaskState
如果在task的根activity中这个属性被设置为“true”,刚才所描述的默认行为不会发生。即使经过长时间,该task仍保留栈中的所有activity。
clearTaskOnLaunch
如果在task的根activity中这个属性被设置为“true”,则只要用户离开并再次返回该 task,栈就会被清理至根 activity。也就是说,正好与alwaysRetainTaskState相反。用户每次返回 task 时看到的都是初始状态,即使只是离开一会儿。
finishOnTaskLaunch
该属性是类似于clearTaskOnLaunch,只是它只对一个 activity 有效,不是整个 task。这能让任何一个 activity 消失,包括 根 activity。如果 activity 的此属性设为 "true",则只会保留 task 中当前 session 所涉及的内容。如果用户离开后再返回 task,它就不存在了。

启动task

你可以把某个 activity 设为 task 的入口,通过发送一个 action 为 "android.intent.action.MAIN"、category 为"android.intent.category.LAUNCHER" 的 intent即可。例如:

<activity ... > 
   
<intent-filter ... >
       
<action android:name = "android.intent.action.MAIN" />
       
<category android:name = "android.intent.category.LAUNCHER" />
   
</intent-filter>
    ...
</activity>

这样的一种intent过滤器导致activity的图标和标签显示在application launcher中,给用户一种方式来启动activity,并且在之后任何时候返回其启动时的 task

这第二种功能很重要:用户必须能够离开一个task,然后能返回来使用这个启动task的activity。由于这个原因,“singleTask”和 “singleInstance”两个 launch modes 标志着activity总是开始一个task ,应该仅被应用于当activity具有 ACTION_MAINCATEGORY_LAUNCHER滤波器时。可以想象一下,例如,如果该过滤器未被设置会发生什么:一个intent启动一个启动模式为“singleTask”的activity,启动一个新的任务,用户在此task中操作了一段时间。然后,用户按下主页 按钮,现在的task是就会转到后台,并且是不可见的。现在,用户没有办法返回到此task种,因为它没有在application launcher中配置相关的属性。

对于这些情况,你不希望用户能够返回到一个activity,设置 <activity>元素的 finishOnTaskLaunch 为“true”(见 Clearing the stack)。

关于task和activity在 overview screen中是如何体现和管理的更多信息,请参阅Overview Screen。


http://developer.android.com/guide/components/tasks-and-back-stack.html

0 0
原创粉丝点击