任务和返回堆栈

任务是用户在尝试执行某些操作时与之互动的一系列 activity 执行某些操作这些 activity 排列在一个称为 返回堆栈,按每个 activity 打开的顺序排列。

例如,电子邮件应用 可能有一个 activity 来显示新邮件列表。当用户选择 消息,系统会打开一个新的活动来查看该消息。这项新活动已添加 返回堆栈。然后,当用户点按“返回”或做出“返回”手势时,这个新 activity 从堆栈中弹出

任务及其返回堆栈的生命周期

大多数任务都从设备主屏幕上启动。当用户触摸屏幕时 应用启动器中或主屏幕上的应用或快捷方式的图标; 该应用的任务进入前台。如果应用程序不存在任务,则 系统会创建一项新任务,并且主 activity 作为堆栈中的根 activity。

在当前 activity 启动另一个 activity 时,新 activity 会推送到顶部 并且会获得焦点上一个 activity 仍保留在堆栈中, 停止。当某个 activity 停止时,系统会保留其 界面。当用户执行返回操作时,当前 activity 为 从堆栈顶部弹出并销毁。通过 之前的 activity 会恢复,而其界面的前一状态会恢复。

以下位置中的活动: 从未被重新排列,只会按原样推送到堆栈中和从堆栈中弹出 由当前 activity 启动并被用户关闭 返回按钮或手势因此,返回堆栈以 采用后进先出的对象结构。图 1 显示了一个带有 被推送到返回堆栈并从返回堆栈弹出的 activity。

图 1. 有关任务中的每个新 Activity 如何添加到返回堆栈的图示。当用户点按或手势时 返回,当前 activity 被销毁,前一个 activity 继续。

当用户继续点按或做出“返回”手势时,堆栈中的每个 activity 会弹出上一个窗口,直到用户返回首页 屏幕或任务开始时正在运行的任何 activity。当所有 从堆栈中移除 activity,则任务将不存在。

根启动器 activity 的返回点按行为

根启动器 activity 是声明 intent 的 activity 和过滤条件 ACTION_MAINCATEGORY_LAUNCHER。 这些 activity 独一无二,因为它们充当着从 Google Play 下载的 应用启动器,用于启动任务

当用户从根启动器 activity 中点按或做出“返回”手势时,系统 处理事件的方式会有所不同,具体取决于 设备正在运行。

Android 11 及更低版本上的系统行为
系统完成 activity。
Android 12 及更高版本上的系统行为

系统会将 activity 及其任务移至后台,而不是 以及结束活动此行为与默认系统行为一致, 使用主屏幕按钮或手势离开应用。

在大多数情况下,这种行为意味着用户可以更快地恢复您的应用 而不是从温状态开始 必须从冷启动时间 状态

如果您需要提供自定义返回导航, 我们建议使用 AndroidX Activity API,而不是替换 onBackPressed()。AndroidX Activity API 会自动遵循 如果没有组件拦截系统,则相应的系统行为 点按背面。

但是,如果您的应用替换 onBackPressed() 用于处理 返回导航并完成 activity,更新您的实现以调用 一直到super.onBackPressed(),而不是完成。正在呼叫 super.onBackPressed() 在以下情况下会将 activity 及其任务移至后台: 并且能为用户提供更加一致的导航体验 。

后台任务和前台任务

图 2. 两个任务:任务 B 在 在前台运行,而任务 A 在后台等待 继续。

任务是一个有机整体,在用户开始 新任务或转到主屏幕。在后台运行时,所有 activity 停止,但任务的返回堆栈仍然保持不变,即任务 失去焦点,如图 2 所示。答 然后,任务可以返回到前台,以便用户可以从离开的位置继续 关闭。

假设当前任务 A 的以下任务流程 其堆栈中有三个 activity,包括当前 activity 下的两个:

  1. 用户使用主屏幕按钮或手势,然后从 应用启动器。

    出现主屏幕时,任务 A 进入后台。当新的 应用启动时,系统会为该应用启动一个具有自己的堆栈的任务(任务 B) 活动数。

  2. 与该应用互动后,用户再次返回主屏幕并选择 应用程序。

    现在,任务 A 进入前台,其堆栈中的所有三个 activity 并且堆栈顶部的 activity 将恢复。此时, 用户还可以通过前往主屏幕并选择应用图标来切换回任务 B 或是从最近使用的应用列表中 screen

多个 activity 实例

图 3. 一个 activity 可以多次实例化 。

由于返回堆栈中的 activity 永远不会重新排列,因此如果您的应用 可让用户从多个 activity 启动特定 activity,新的 创建该 activity 的实例并将其推送到堆栈,而不是 将之前该 activity 的所有实例置于顶部。因此, 应用中的 activity 可能会多次实例化,即使 如图 3 所示。

如果用户使用“返回”按钮向后导航 按钮或手势,activity 实例就会按其顺序显示 每个对象都有自己的界面状态。不过,您可以将此 行为。了解 有关详情,请参阅管理 任务

多窗口环境

当应用在多窗口模式中同时运行时 环境,在 Android 7.0 (API) 中受支持 级别 24)及更高版本,系统会单独管理每个窗口的任务。每个 窗口中可以包含多个任务同样,在 Android 设备上运行的 Android 应用也应如此 Chromebook:系统会在 按窗口统计

生命周期回顾

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

  • 当 activity A 启动 activity B 时,activity A 会停止,但系统会 保留其状态,例如滚动位置和输入到表单中的任何文本。如果 用户在 activity B、activity A 中点按或使用“返回”手势 恢复并恢复其状态

  • 当用户使用主屏幕按钮或手势离开任务时,当前的 activity 会停止,其任务会转到后台。系统会保留 任务中每个 activity 的状态。如果用户稍后通过以下方法来恢复任务: 选择开始该任务的启动器图标时,任务会进入 在前台运行,并在堆栈顶部恢复 activity。

  • 如果用户点按“返回”或做出“返回”手势,当前 activity 会从 堆栈并将其销毁堆栈中的上一个 activity 会恢复。时间 当 activity 被销毁时,系统不会保留该 activity 的状态。

    此行为与根启动器 activity 不同 当应用在搭载 Android 12 或更高版本的设备上运行时。

  • Activity 可以多次实例化,甚至是从其他任务对其进行实例化。

管理任务

Android 通过以下方式管理任务和返回堆栈: 所有 activity 都在同一任务中相继启动,最后一个为第一个, 输出堆栈。这对于大多数应用都非常有效,您通常无需担心 activity 与任务的关联方式,或任务在后端的存在方式 堆栈。

不过,您可能会决定要中断正常行为。 例如,您可能希望应用中的某个 activity 在 activity 被触发时启动新任务, 开始, 而不是放置在当前任务中。或者,当您启动 则您可能需要调出它的现有实例,而不是 在返回堆栈之上创建新实例的方法。或者,您可能 希望返回堆栈中清除除根 activity 之外的所有 activity 当用户离开任务时触发。

您可以使用 <activity> 清单元素 和标记 startActivity()

以下是可用于管理任务的主要 <activity> 属性:

以下是您可以使用的主要 intent 标志:

以下部分讨论如何使用这些清单属性 和 intent 标志来定义 activity 与任务的关联方式以及 在返回堆栈中的行为

此外,还讨论了与任务和活动 在“最近使用的应用”屏幕中进行显示和管理。通常,您可以让 系统定义任务和 activity 在 “最近使用的应用”屏幕,您无需修改此行为。有关 信息,请参阅“最近使用的应用”屏幕

定义启动模式

通过启动模式,您可以定义如何关联新的 activity 实例 与当前任务相关联。您可以通过两种方式定义启动模式 :

因此,如果 activity A 启动 activity B,activity B 可以在其清单中定义 activity A 可以使用 intent 标志, 请求 activity B 如何与当前任务关联。

如果两者 activity 定义了 activity B 与任务的关联方式,然后 activity A 的 请求(如 intent 中所定义)优先于 activity B 的请求,如 在其清单中定义的任何订单。

使用清单文件定义启动模式

在清单文件中声明 Activity 时,您可以指定 使用 <activity> 元素的 launchMode 属性。

您可以为 launchMode 属性指定五种启动模式:

  1. "standard"
    默认模式。系统会在任务中创建新的 activity 实例 以及将 intent 传递给它。活动可以是 实例化多次,每个实例可以属于不同的任务,以及 一个任务可以有多个实例。
  2. "singleTop"
    如果当前任务的顶部已存在 activity 的实例, 系统会通过调用 onNewIntent() 方法,而不是创建新的 Activity 实例。此活动为 实例化多次,每个实例可以属于不同的任务, 一个任务可以有多个实例(但前提是顶部的 activity 不是 activity 的现有实例)。

    例如,假设任务的返回堆栈由根 activity A 和 activity B、activity C 和 activity D 在顶部(因此堆栈为 A-B-C-D,D 在顶部)。intent 。如果 D 具有默认的 "standard" 启动 模式,系统会启动该类的新实例,并且堆栈会变为 A-B-C-D-D。 不过,如果 D 的启动模式为 "singleTop",则 D 的现有实例 通过 onNewIntent() 接收 intent, 因为它位于堆栈顶部,而堆栈仍旧是 A-B-C-D。如果 另一方面 intent 到达类型 B 的 activity,则系统会将 B 的新实例添加到 堆栈中,即使其启动模式为 "singleTop"

  3. "singleTask"
    系统会在新任务的根位置创建 activity 或找到 具有同一相似性的现有任务上的 activity。如果 已存在,则系统会将 intent(通过调用其 onNewIntent() 方法,而不是创建新实例。同时,所有其他 其上的 activity 会被销毁。
  4. "singleInstance"
    此行为与 "singleTask" 相同,只是系统不会启动任何其他 将 activity 放入包含该实例的任务中。activity 始终是 是其任务中的唯一一个成员。由此活动启动的所有活动都会在 一个单独的任务。
  5. "singleInstancePerTask"
    activity 只能作为任务的根 activity(第一个 activity)运行 activity,因此只能有一个实例 此 activity 的实际情况。与 singleTask 启动模式相比,以下代码 在不同任务的多个实例中启动 activity,如果 FLAG_ACTIVITY_MULTIPLE_TASKFLAG_ACTIVITY_NEW_DOCUMENT 标志已设置。

再举一个例子,Android 浏览器应用声明,网络浏览器 通过指定 singleTask,activity 始终在自己的任务中打开 <activity>中的启动模式 元素。这意味着,如果您的应用发出用于打开 Android 浏览器时,其 activity 不会与您的应用放在同一任务中。相反, 将为浏览器启动新任务;如果浏览器已有任务 在后台运行时,系统会将该任务上移一层以处理新的 intent。

无论 activity 是在新任务中启动还是在同一任务中启动,都是如此 启动它的 activity 时,返回按钮和手势始终会 转至上一个 activity。不过,如果你启动一个 指定了 singleTask 启动模式,并且该 activity 的一个实例存在于 后台任务,那么整个任务都会转至前台。此时, 返回堆栈包含在 堆栈顶部图 4 显示了这种情况。

图 4. activity 启动后图示 模式 "singleTask" 会添加到返回堆栈中。如果活动 已经属于具有自己的返回堆栈的后台任务,那么 整个返回堆栈也会向前推进, 任务。

如需详细了解如何在清单文件中使用启动模式,请参阅 <activity> 元素文档。

使用 Intent 标志定义启动模式

启动 activity 时,您可以修改 activity 的默认关联 通过在向其传递的 intent 中添加标志, startActivity()。 可用于修改默认行为的标志如下:

FLAG_ACTIVITY_NEW_TASK

系统会在新任务中启动 activity。如果已有为 activity 启动后,系统会将该任务转到前台运行,并向其发出 恢复最后状态,并且 activity 在 onNewIntent()

这会生成与 "singleTask" 讨论了 launchMode 值 。

FLAG_ACTIVITY_SINGLE_TOP

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

这会生成与 "singleTop" launchMode 值。

FLAG_ACTIVITY_CLEAR_TOP

如果要启动的 activity 已在当前任务中运行, 则系统不会启动该 activity 的新实例,而是会 系统会销毁其上面的所有其他 activity。其意图是 传递到 Activity 的已恢复实例(现在位于顶层) onNewIntent()

launchMode 属性没有可产生此行为的值。

FLAG_ACTIVITY_CLEAR_TOP 最常与以下元素结合使用: FLAG_ACTIVITY_NEW_TASK。这些标志组合使用时 找到另一个任务中的现有 activity,并将其放到某个位置 响应意图。

"standard"

处理亲和性

“亲和性”表示 activity“首选”哪项任务哪些对象。修改者 默认情况下,同一应用中的所有 activity 彼此相似: 他们“更喜欢”位于同一任务中。

不过,您可以修改 activity 的默认相似性。定义的 activity 在不同应用内可以具有共同的兴趣相似受众群体,并且在同一应用中定义的 activity 可以分配不同的任务相似性。

您可以使用 taskAffinity 修改 activity 的相似性 <activity> 的属性 元素。

taskAffinity 属性接受的字符串值必须不同于 默认软件包名称 在 <manifest> 中声明 元素,因为系统会使用该名称标识默认任务 用户的应用体验

亲和性可在两种情况下发挥作用:

  1. 当启动 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 标志。

    默认情况下,新 activity 会启动到该 activity 的任务中 名为 startActivity()。 它将被推送到与调用方相同的返回堆栈中。

    不过,如果传递给 startActivity() 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标志,系统会查找其他任务来存放新 activity。通常情况下 这是一个新任务不过,不一定非要这么做。如果有 具有与新 activity 具有相同相似性的现有任务,即 activity 启动到该任务中。如果不存在,则会启动一个新任务。

    如果此标志导致 Activity 启动新任务,并且用户使用 使用主屏幕按钮或手势离开主屏幕,则必须为用户提供某种方式 返回到任务有些实体(例如通知管理器) 始终在外部任务中启动 activity,而不是作为其自身的一部分启动,因此 它们始终将 FLAG_ACTIVITY_NEW_TASK 放入传递给它们的 intent 中 startActivity()

    如果外部实体可能 使用此标志可以调用您的 activity,请注意,用户具有 可让您更轻松地 已启动的任务,例如带有启动器图标, 该任务包含 CATEGORY_LAUNCHER intent 过滤器。如需了解详情,请参阅启动任务部分。

  2. 当 activity 具有 allowTaskReparenting 时 属性设置为 "true"

    在这种情况下,activity 可以从其启动的任务移至其所在的任务 该任务在前台运行。

    例如,假设一个 activity 报告天气状况, 选定城市定义为旅游应用的一部分。具有相同的亲和性 默认应用亲和性 通过此属性更改父项。

    当您的某个 activity 启动 它最初同属于您的 活动。但是,当旅行应用的任务进入前台时, 天气预报活动将被重新分配给该任务,并显示在其中。

清除返回堆栈

如果用户长时间离开任务,系统会清除所有 根 activity 之外的 activity。当用户返回任务时 只会恢复根 activity。这取决于 我们假设 用户放弃正在做的操作的时间越来越长 然后返回任务以开始新的操作。

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

alwaysRetainTaskState
在任务的根 activity 中将此属性设为 "true" 时, 而不会发生上述默认行为任务会保留所有 即使是在很长时间过后仍出现在堆栈中的 activity。
clearTaskOnLaunch

在任务的根 activity 中将此属性设置为 "true" 时,任务 每当用户离开任务时,系统都会清除到根 activity, 返回给它。也就是说, alwaysRetainTaskState。通过 用户始终会返回到任务的初始状态, 任务。

finishOnTaskLaunch

此属性类似于 clearTaskOnLaunch, 但它只对单个 activity 运行,而不是对整个任务执行操作。还会导致 任何要完成的 activity(根 activity 除外)。设置为 "true" 时,activity 仍是任务的一部分,但仅限于当前会话。 如果用户离开任务再返回,该任务将不复存在。

启动任务

您可以通过向 activity 提供 intent 将其设为任务的入口点 将 "android.intent.action.MAIN" 作为指定操作的过滤器, 指定类别为 "android.intent.category.LAUNCHER"

<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。因此,请仅使用 二 用于将 activity 标记为始终启动任务的启动模式,"singleTask""singleInstance",当 activity 具有 ACTION_MAINCATEGORY_LAUNCHER 过滤。

例如,想象一下,如果缺少过滤器,会发生什么情况: intent 会启动 "singleTask" activity,从而启动一个新任务,而用户 花一些时间来完成这项任务。然后,用户使用主屏幕按钮或 手势。此时,该任务会转到后台,不再可见。现在,用户 无法返回到该任务,因为它未在应用中表示 启动器。

对于那些您不想让用户返回 activity,请设置 <activity> 元素的 finishOnTaskLaunch 发送至 "true"。如需了解详情,请参阅清除返回堆栈部分。

有关如何表示和管理任务和活动的更多信息 最近使用的应用 screen

更多资源