Tasks and Back Stack

一个应用程序通常包含有多个activity。每个activity的设计都应围绕一个特别的动作展开这样用户可以显示和启动其它的activity。比如,一个email应用程序可能需要一个activity展示新邮件列表。当用户点击了一个email后,打开一个新的activity展示这个email的内容。

activity还可以启动同一设备上其它应用程序的某个activity。例如,某个应用想要发送一封邮件,我们可以定义一个intent响应"send"动作,它可以包含一些数据,比如邮件地址和邮件内容。而另一个应用的某个activity声明自己响应这样的intent。在本例中,这个intent表示发送邮件,所以一个邮件应用的“compose”activity被启动(如果有多个应用支持响应这一intent,那么系统会让用户选择使用哪一个应用)。当邮件发送之后,之前的应用activity恢复,这样发送的这一activity仿佛就如同是同一应用的activity了。尽管这样的activity可能来自不同的应用,安卓系统通过把这两个activity保存在同一任务中提升用户体验。

一个任务表示用户处理一个行为时要交互的一系列activities容器。这些activities按activities先后打开的顺序被保存在任务栈中。

设备主屏幕是大多数任务开始的地方。当用户在应用启动器里点击了应用图标时,相应的任务启动到前台。如果应用的任务不存在(最近该应用未使用),一个新任务被创立,并且该应用的"main"activity作为根activity保存在任务栈中。

当当前activity启动另一个activity时,新启动的activity被push到栈顶并获取焦点,而之前的activity保留在栈中,或停止(分别是什么情况下?)。当一个activity停止时,系统保留当前状态。当用户点击返回按钮时,当前activity从栈顶弹出(activity被摧毁),并且恢复之前的activity(它相应的视图状态恢复)。栈中的activities从不会重新排列,只会进行出栈和入栈操作——被当前activity启动时入栈,当用户点击返回按钮离开时出栈。它遵循后进先出原则。下图显示了相应的操作。

当用户连续点击返回按钮时,栈中的每个activity相继出栈显示之前的activity,直至用户返回主屏幕(或是任务开始的那个activity)。当所有activities从任务栈中删除时,任务不再存在。

一个任务是一个联系密切的单元,当用户点击Home按钮开始一个新任务或回到主屏,它会移动到后台。在后台,任务的所有activities停止,但任务栈依旧存在——因为该任务只是被其他的任务取代了焦点位置而已。如下右图所示。任务可以回到前台,显示它离开时的activity。用户点击Home按钮,从启动器启动一个新的应用。当主屏幕出现时,任务A进入后台,当新应用启动时,系统为之开始一个新任务(任务B)。任务B动作完成后,回到主屏幕选择启动任务A的应用。现在,任务A回到了前台——栈中所有activities保存完好并显示栈顶的activity。同时此时,用户还可以回到主屏幕选择启动任务B(通过长按Home按钮显示最近任务栈选择。)这是一个多任务例子。

Note:后台可以一次拥有多个任务。然而,如果用户在后台运行的任务太多,系统有可能会摧毁它们以回收部分内存,这样会导致activity状态的丢失。

因为任务栈中的activity从不进行重排,如果应用允许用户可以从多个activities启动某个特殊的activity,那么同一个activity的实例就会被创建并入栈(而不是把先前的栈中的那个activity实例挪到顶端)。这样,应用的某个activity就有可能被实例化多次(即使是在不同的任务中)如上左图所示。同时,如果用户点击返回按钮返回,每个实例依然按照它们打开的顺序出现。然而,我们可以阻止这种行为如果我们不希望这个activity被实例话多次的话。这部分内容在Managing Tasks中讨论。

总结activities和任务的缺省行为如下:

  • 当Activity A启动Activity B,Activity A停止,但系统保存这它的状态(比如表格的滚动位置和文本内容)。如果用户在Activity B中点击返回按钮,Activity A重新出现被恢复之前的状态。
  • 当用户通过点击Home按钮离开任务时,当前activity停止,它的任务回到后台。系统保存任务的所有activity状态。如果之后用户点击主屏幕下的该应用恢复任务,该任务又回到前台并且恢复离开是的那个activity到栈顶。
  • 如果用户点击返回按钮,当前activity出栈并被摧毁。栈中之前的activity恢复。当一个activity被摧毁时,系统不保存它的状态。
  • Activities可以被实例化多次,即使是在不同的任务中。

Saving Activity State

如上所讨论,系统的缺省行为是当一个activity停止时,系统保持它的状态。这样的话,当用户返回先前的activity时,用户接口就和之前离开时相同。然而,我们可以——或者说应该——使用回调函数保存状态信息,以保证它们在被摧毁后状态依然保存。

当系统停止了某个activity时(例如启动一个新的activity或者任务移到了后台),系统在需要回收内存时会完全摧毁它,该activity的所有状态信息都丢失。如果这样,系统仍然知道activity在返回栈中有相应的位置,但当该activity回到栈顶时,系统对它重新创建(而不是resume)。为了避免用户丢失数据,我们应该实现onSaveInstanceState()回调。

Managing Tasks

系统管理任务和返回栈的方式,如上所示,放置所有启动成功的activities到一个相同的任务栈中。这一机制相当不错,我们不需要担心activities是如何与任务相关联的,也不需要担心它们在任务栈中的存在方式。然而,我们可能会决定中断某个行为。也许,我们想要某个应用的一个activity启动一个新任务,而不是放置在当前任务中;或者,我们想要启动一个activity,我们想要把它已有的一个实例挪到前台(而不是在栈顶创建一个新的实例);或者,当用户离开任务时我们想要清除返回栈中的除根activity之外的所有activities。

所有的这些都可以通过manifest文件中的<activity>标签设置intent传递到startActivity()实现。<activity>属性可以使用的有:

taskAffinity,launchMode,allowTaskReparenting,clearTaskOnLaunch,alwaysRetainTaskState,finishOnTaskLaunch

intent可以携带的标志有:

FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_CLEAR_TOP,FLAG_ACTIVITY_SINGLE_TOP

下面的部分,将会看到如何使用manifest属性和intent标志位定义activities和相关的任务已经它们在任务栈中的行为。

Caution:大多数应用都不应该打断activities和任务的缺省行为。如果我们有必要这样做,小心使用并验证在启动器中和使用返回按钮从其它activities返回时的可行性。一定要验证缺省行为和用户期待行为的冲突。

Defining launch modes

Launch modes允许我们定义当前任务相关的activity新的实例。可以使用两种方式定义launch modes。

在manifest文件中定义。

当我们在manifest中声明了一个activity时,我们可以指定它启动时与任务的联系方式。

使用Intent标志位

当调用startActivity()时,Intent可以包括一个标志位申明启动的activity与当前任务的联系方式。

这样,当Activity A启动Activity B时,Activity B在manifest中定义了它与当前任务的联系方式,而Activity A同时可以要求Activity B与当前任务的联系方式。如果两个Activity都定义了Activity B与当前任务的联系方式,那么当Activity A要求时,系统优先考虑Activity A的要求。

Note:一些launch modes允许在manifest中定义,而不允许使用Intent标志位,与之相反,一些launch modes允许使用Intent标志位定义而不允许在manifest中定义。

Using the manifest file

在manifest文件中申明一个activity时,可以使用<activity>标签的launchMode属性指定activity与任务的联系方式。

"standard"(缺省模式):系统创建activity的新实例到当前所在的任务中。该activity可以被实例化多次,每个实例可以属于不同的任务,一个任务可以有多个实例。

"singleTop":如果当前任务栈顶已经存在一个该activity实例,系统通过调用onNewIntent()把intent导入到该实例,而不是创建一个新的实例。该activity可以被实例化多次,每个实例可以属于不同的任务,一个任务可以有多个实例(只有该activity在栈顶时,不会创建新的实例)。

例如,假设任务栈由根activity A,activity B,C和栈顶的D组成(栈为A-B-C-D,D在栈顶)。此时到达一个D的intent。如果D的launch mode为"standard",该类的新实例被创建,所以该栈先在为A-B-C-D-D。然而如果D的launch mode为"singleTop",已存在的D实例通过onNewIntent()接收到intent。因为它在栈顶,所以,该栈仍为A-B-C-D,然而,如果此时来的是B的intent,那么尽管B的launch mode为"singleTop",仍然会创建B的实例到栈顶。

Note:当创建了一个activity新实例时,用户可以点击返回按钮回到之前的activity。但是当一个已经存在的实例处理一个新的intent时,用户不能够在该intent到达onNewIntent()之前点击返回按钮回到之前的状态。

"singleTask":系统创建一个新任务并初始化这个activity为新任务的根。然而,如果该activity已经在其它任务中存在实例,那么系统将会把这个intent通过onNewIntent()导入到已存在的那个实例,而不是创建一个新的实例。一次只允许有一个该activity实例。

Note:尽管activity启动一个新的任务,点击返回按钮时仍然回到用户之前的那个activity。

"singleInstance":同"singleTask",除了系统不会在它所在的任务中创建任何其它activity实例。也即该activity是该任务的唯一成员。它启动的其它activity都在其它任务中。

作为一个例子,安卓浏览器应用申明web browser activity只在它自己的任务中打开——它声明为"singleTask"模式。这意味着如果你的一个应用要打开浏览器,这个activity不会放置到你应用所在的任务中。

不管一个activity启动一个新任务或者在同一个任务中,返回按钮将把用户带到之前的那个activity。然而,如果启动一个指定了"singleTask"的activity。那么如果它的一个实例已经存在于后台任务中,整个任务就被提到了前台。这样的话,返回栈包括任务中的所有activities。如下图所示:

注意:在manifest中声明的这些launch mode都可以被intent 标志位方式覆盖。

Using Intent flags

FLAG_ACTIVITY_NEW_TASK:同之前的"singleTask",activity在onNewIntent()回调中接收新的intent。

FLAG_ACTIVITY_SINGLE_TOP:同之前的"singleTop",activity在onNewIntent()回调中接收新的intent。

FLAG_ACTIVITY_CLEAR_TOP:manifest中没有对应的属性,如果要启动的activity已经运行在当前任务中,它不创建新的实例,而是摧毁所有在栈中在它顶部的activity通过onNewIntent()把它送到栈顶。

Handling affinities

affinity指示了一个activity倾向属于的任务。缺省情况下,同一应用的所有activities彼此有affinity。所以缺省情况下,同一应用的所有activities属于同一任务。然而我们可以修改一个activity的缺省affinity,也可以让不同应用的activities分享同一affinity,或者让同一应用中的activities分配不同任务的。

我们可以通过<activity>标签下的taskAffinity属性更改它的affinity。taskAffinity属性有一个string值,它是在<manifest>标签下声明的缺省包名的特殊值,系统使用它来认证应用的缺省任务affinity。

affinity在两种情景下:

  • 使用intent启动的activity包含有FLAG_ACTIVITY_NEW_TASK标签。这个标签意味着系统将要开辟一个新任务创建这个activity。通常,如果已经有了一个任务同这个activity有相同的affinity,那么这个activity添加到这个task,如果不是,则开启新的任务。当这个标志位导致activity启动了一个新任务并且用户点击Home按钮离开它时,必须有一种方式让用户可以返回到这个任务。一些entities(比如notification manager)总是在一个外部任务中启动activities,而不是在它们自己的activities中,所以我们通常把FLAG_ACTIVITY_NEW_TASK放置在intent中传递给startActivity()。
  • 当一个activity的allowTaskReparenting属性值为真时,这个activity可以在从启动它的任务移到和它有相同affinity的回到前台的任务中。例如,假设一个旅行应用的某个activity报告各个选定城市的天气播报界面。它和这个应用中的其它activities拥有相同的affinity(应用缺省affinity)。当你的某个activities启动了这个天气播报activity,它属于你的这个activity所在的任务。然而,当旅行应用回到前台时,天气播报activity被安排到这个任务中。

Clearing the back stack

如果用户离开一个任务的时间太久,系统会清除掉任务中除根activity之外的其它所有activities。当用户重新回到任务中时,只有根activity得到了恢复。系统这样做是因为,在离开很长时间之后,用户通常可能希望禁止他们之前的行为而重新开始。

以下三个activity属性控制这一行为:

alwaysRetainTaskState:如果根activity的该值设为真,那么任务返回的就不只根activity,而是所有activity。

clearTaskOnLaunch:如果根activity的该值设为真,那么只要用户离开这一任务,返回时都只返回根activity,其他activity被清除。

finishOnTaskLaunch:这一值作用于每个单独的activity,当它设为真时,只要用户离开了任务,所有设置这个值为真的activity都被清除,包括根activity。

Starting a task

我们可以设置应用的某个activity为应用入口。这个activity必须包含如下的这个<intent-filter>:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值