任务、返回栈及启动模式

一、任务和返回栈

应用通常包含多个 Activity。每个 Activity 均应围绕用户可以执行的特定操作设计,并且能够启动其他 Activity。一个 Activity 甚至可以启动设备上其他应用中存在的 Activity。

任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。

设备主屏幕是大多数任务的起点。当用户触摸应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务将出现在前台。 如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的“主”Activity 将作为堆栈中的根 Activity 打开。

当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 堆栈中的 Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。 因此,返回栈以“后进先出”对象结构运行。 图 1 通过时间线显示 Activity 之间的进度以及每个时间点的当前返回栈,直观呈现了这种行为。

图 1. 显示任务中的每个新 Activity 如何向返回栈添加项目。 用户按“返回”按钮时,当前 Activity 随即被销毁,而前一个 Activity 恢复执行

如果用户继续按“返回”,堆栈中的相应 Activity 就会弹出,以显示前一个 Activity,直到用户返回主屏幕为止(或者,返回任务开始时正在运行的任意 Activity)。 当所有 Activity 均从堆栈中移除后,任务即不复存在。

任务是一个有机整体,当用户开始新任务或通过“主页”按钮转到主屏幕时,可以移动到“后台”。 尽管在后台时,该任务中的所有 Activity 全部停止,但是任务的返回栈仍旧不变,也就是说,当另一个任务发生时,该任务仅仅失去焦点而已,如图 2 中所示。然后,任务可以返回到“前台”,用户就能够回到离开时的状态。

图 2. 两个任务:任务 B 在前台接收用户交互,而任务 A 则在后台等待恢复

Activity 和任务的默认行为总结如下:

1)当 Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。

2)用户通过按“主页”按钮离开任务时,当前 Activity 将停止且其任务会进入后台。 系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。

3)如果用户按“返回”按钮,则当前 Activity 会从堆栈弹出并被销毁。 堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统不会保留该 Activity 的状态。

4)即使来自其他任务,Activity 也可以多次实例化。

二、管理任务

Android 管理任务和返回栈的方式(如上所述,即:将所有连续启动的 Activity 放入同一任务和“后进先出”堆栈中)非常适用于大多数应用,而您不必担心 Activity 如何与任务关联或者如何存在于返回栈中。 但是,您可能会决定要中断正常行为。 也许您希望应用中的 Activity 在启动时开始新任务(而不是放置在当前任务中);或者,当启动 Activity 时,您希望将其现有实例上移一层(而不是在返回栈的顶部创建新实例);或者,您希望在用户离开任务时,清除返回栈中除根 Activity 以外的所有其他 Activity。

通过使用 <activity> 清单文件元素中的属性和传递给 startActivity() 的 Intent 中的标志,您可以执行所有这些操作以及其他操作。

1.定义启动模式

启动模式允许您定义 Activity 的新实例如何与当前任务关联。 您可以通过两种方法定义不同的启动模式:

使用清单文件:在清单文件中声明 Activity 时,您可以指定 Activity 在启动时应该如何与任务关联。

使用 Intent 标志:调用 startActivity() 时,可以在 Intent 中加入一个标志,用于声明新 Activity 如何(或是否)与当前任务关联。

:某些适用于清单文件的启动模式不可用作 Intent 标志,同样,某些可用作 Intent 标志的启动模式无法在清单文件中定义。

1)使用清单文件

在清单文件中声明 Activity 时,您可以使用 <activity> 元素的 launchMode 属性指定 Activity 应该如何与任务关联。launchMode 属性指定有关应如何将 Activity 启动到任务中的指令。您可以分配给 launchMode 属性的启动模式共有四种:

"standard" 标准模式:(默认模式)默认。系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。

"singleTop" 栈顶复用模式:如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。

:为某个 Activity 创建新实例时,用户可以按“返回”按钮返回到前一个 Activity。 但是,当 Activity 的现有实例处理新 Intent 时,则在新 Intent 到达 onNewIntent() 之前,用户无法按“返回”按钮返回到 Activity 的状态。

"singleTask" 栈内复用模式:系统创建新任务并实例化位于新任务底部的 Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。

:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity。

注解:1)如果Activity在目标任务栈里则直接调用该activity实例的onNewIntent()方法。如果任务栈中它的上方还存在别的activity,那么这些activity会被全部弹出栈。2)如果Activity不在目标任务栈里,系统会新创建一个任务,并将该activity实例化作为该任务的根activity。

"singleInstance" 单实例模式:与 "singleTask" 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。

注解:全局单例模式,只要整个系统中创建或此Activity实例不再创建。这个经常使用于系统中的应用,比如Launch、锁屏键的应用等等,整个系统中仅仅有一个。

无论 Activity 是在新任务中启动,还是在与启动 Activity 相同的任务中启动,用户按“返回”按钮始终会转到前一个 Activity。 但是,如果启动指定 singleTask 启动模式的 Activity,则当某后台任务中存在该 Activity 的实例时,整个任务都会转移到前台。此时,返回栈包括上移到堆栈顶部的任务中的所有 Activity。 图 4 显示了这种情况。

图 4. 显示如何将启动模式为“singleTask”的 Activity 添加到返回栈。 如果 Activity 已经是某个拥有自己的返回栈的后台任务的一部分,则整个返回栈也会上移到当前任务的顶部

使用 Intent 标志

启动 Activity 时,您可以通过在传递给 startActivity() 的 Intent 中加入相应的标志,修改 Activity 与其任务的默认关联方式。可用于修改默认行为的标志包括:

FLAG_ACTIVITY_NEW_TASK

在新任务中启动 Activity。如果已为正在启动的 Activity 运行任务,则该任务会转到前台并恢复其最后状态,同时 Activity 会在 onNewIntent() 中收到新 Intent。

正如前文所述,这会产生与 "singleTask"launchMode 值相同的行为。

FLAG_ACTIVITY_SINGLE_TOP

如果正在启动的 Activity 是当前 Activity(位于返回栈的顶部),则 现有实例会接收对 onNewIntent() 的调用,而不是创建 Activity 的新实例。

正如前文所述,这会产生与 "singleTop"launchMode 值相同的行为。

FLAG_ACTIVITY_CLEAR_TOP

如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。

产生这种行为的 launchMode 属性没有值。

FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK 结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。

2.清理返回栈

如果用户长时间离开任务,则系统会清除所有 Activity 的任务,根 Activity 除外。 当用户再次返回到任务时,仅恢复根 Activity。系统这样做的原因是,经过很长一段时间后,用户可能已经放弃之前执行的操作,返回到任务是要开始执行新的操作。

您可以使用下列几个 Activity 属性修改此行为:

alwaysRetainTaskState

如果在任务的根 Activity 中将此属性设置为 "true",则不会发生刚才所述的默认行为。即使在很长一段时间后,任务仍将所有 Activity 保留在其堆栈中。

clearTaskOnLaunch

如果在任务的根 Activity 中将此属性设置为 "true",则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。 换而言之,它与 alwaysRetainTaskState 正好相反。 即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。

finishOnTaskLaunch

此属性类似于 clearTaskOnLaunch,但它对单个 Activity 起作用,而非整个任务。 此外,它还有可能会导致任何 Activity 停止,包括根 Activity。 设置为 "true" 时,Activity 仍是任务的一部分,但是仅限于当前会话。如果用户离开然后返回任务,则任务将不复存在。

3.启动任务

通过为 Activity 提供一个以 "android.intent.action.MAIN" 为指定操作、以 "android.intent.category.LAUNCHER" 为指定类别的 Intent 过滤器,您可以将 Activity 设置为任务的入口点。 例如:

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

...

</activity>

此类 Intent 过滤器会使 Activity 的图标和标签显示在应用启动器中,让用户能够启动 Activity 并在启动之后随时返回到创建的任务中。

第二个功能非常重要:用户必须能够在离开任务后,再使用此 Activity 启动器返回该任务。 因此,只有在 Activity 具有ACTION_MAIN 和 CATEGORY_LAUNCHER 过滤器时,才应该使用将 Activity 标记为“始终启动任务”的两种启动模式,即 "singleTask" 和 "singleInstance"。例如,我们可以想像一下如果缺少过滤器会发生什么情况: Intent 启动一个 "singleTask" Activity,从而启动一个新任务,并且用户花了些时间处理该任务。然后,用户按“主页”按钮。 任务现已发送到后台,而且不可见。现在,用户无法返回到任务,因为该任务未显示在应用启动器中。

如果您并不想用户能够返回到 Activity,对于这些情况,请将 <activity> 元素的 finishOnTaskLaunch 设置为 "true"(请参阅清理堆栈)。

三、启动模式的实际应用场景

这四种模式中的Standard模式是最普通的一种,没有什么特别注意。而SingleInstance模式是整个系统的单例模式,在我们的应用中一般不会应用到。所以,这里就具体解说 SingleTop 和 SingleTask模式的运用场景:

1. SingleTask模式的运用场景

最常见的应用场景就是保持我们应用开启后仅仅有一个Activity的实例。最典型的样例就是应用中展示的主页(Home页)。

假设用户在主页跳转到其他页面,运行多次操作后想返回到主页,假设不使用SingleTask模式,在点击返回的过程中会多次看到主页,这明显就是设计不合理了。

2. SingleTop模式的运用场景

假设你在当前的Activity中又要启动同类型的Activity,此时建议将此类型Activity的启动模式指定为SingleTop,能够降低Activity的创建,节省内存。

四、参考原文

https://developer.android.google.cn/guide/components/tasks-and-back-stack

http://www.cnblogs.com/claireyuancy/p/7387696.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容