安卓复习.Part2

任务和返回堆栈

任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。

如果用户按返回按钮,这个新的 Activity 即会完成并从堆栈中退出。

Android 7.0(API 级别 24)及更高版本支持多窗口环境(即分屏操作),当应用在这种环境中同时运行时,系统会单独管理每个窗口的任务;而每个窗口可能包含多项任务。

如果打开的某个任务是之前没有存在过的则,创建新的任务并将该应用的主Activity作为堆栈的根Activity打开。

返回堆栈的Activity顺序

在当前 Activity 启动另一个 Activity 时,新的 Activity 将被推送到堆栈顶部并获得焦点。上一个 Activity 仍保留在堆栈中,但会停止。当 Activity 停止时,系统会保留其界面的当前状态。当用户按返回按钮时,当前 Activity 会从堆栈顶部退出(该 Activity 销毁),上一个 Activity 会恢复(界面会恢复到上一个状态)。**堆栈中的 Activity 永远不会重新排列,只会被送入和退出,在当前 Activity 启动时被送入堆栈,在用户使用返回按钮离开时从堆栈中退出。**因此,返回堆栈按照“后进先出”的对象结构运作。

示意图:

在这里插入图片描述

如果用户继续按返回,则堆栈中的 Activity 会逐个退出,以显示前一个 Activity,直到用户返回到主屏幕(或任务开始时运行的 Activity)。移除堆栈中的所有 Activity 后,该任务将不复存在。

任务的切换

在这里插入图片描述

任务是一个整体单元,当用户开始一个新任务或通过主屏幕按钮进入主屏幕时,任务可移至“后台”。**在后台时,任务中的所有 Activity 都会停止,但任务的返回堆栈会保持不变,当其他任务启动时,当前任务只是失去了焦点。**这样一来,任务就可以返回到“前台”,以便用户可以从他们离开的地方继续操作。举例来说,假设当前任务(任务 A)的堆栈中有 3 个 Activity,当前 Activity 下有 2 个 Activity。用户按主屏幕按钮,然后从应用启动器中启动新应用。主屏幕出现后,任务 A 转到后台。当新应用启动时,系统会启动该应用的任务(任务 B),该任务具有自己的 Activity 堆栈。与该应用互动后,用户再次返回到主屏幕并选择最初启动任务 A 的应用。现在,任务 A 进入前台,其堆栈中的所有三个 Activity 都完好如初,堆栈顶部的 Activity 恢复运行。此时,用户仍可通过以下方式切换到任务 B:转到主屏幕并选择启动该任务的应用图标(或者从最近使用的应用屏幕中选择该应用的任务)

值得注意的是,多个任务可以同时在后台进行。但是,如果用户同时运行很多后台任务,系统可能会为了恢复内存而开始销毁后台 Activity,导致 Activity 状态丢失。

某个Activity被多次实例化

在这里插入图片描述

**由于返回堆栈中的 Activity 不会被重新排列,如果您的应用允许用户从多个 Activity 启动特定的 Activity,系统便会创建该 Activity 的新实例并将其推送到堆栈中(而不是将该 Activity 的某个先前的实例移至堆栈顶部)。**这样一来,应用中的一个 Activity 就可能被多次实例化(甚至是从其他任务对其进行实例化)。因此,如果用户使用返回按钮向后导航,Activity 的每个实例将按照它们被打开的顺序显示出来(每个实例都有自己的界面状态)。不过,如果您不希望某个 Activity 被实例化多次,可以修改此行为。

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

  • 当 Activity A 启动 Activity B 时,Activity A 会停止,但系统会保留其状态(例如滚动位置和输入到表单中的文本)。如果用户在 Activity B 中按返回按钮,系统会恢复 Activity A 及其状态。
  • 当用户通过按主屏幕按钮离开任务时,当前 Activity 会停止,其任务会转到后台。系统会保留任务中每个 Activity 的状态。如果用户稍后通过点按该任务的启动器图标来恢复该任务,该任务会进入前台并恢复堆栈顶部的 Activity。
  • 如果用户按返回按钮,当前 Activity 将从堆栈中退出并销毁。堆栈中的上一个 Activity 将恢复。Activity 被销毁后,系统不会保留该 Activity 的状态。
  • Activity 可以多次实例化,甚至是从其他任务对其进行实例化。

任务管理

如果希望应用中的某个 Activity 在启动时开启一个新的任务(而不是被放入当前的任务中),或者当启动某个 Activity 时,希望调用它的一个现有实例(而不是在返回堆栈顶部创建一个新实例),或者希望在用户离开任务时清除返回堆栈中除根 Activity 以外的所有 Activity。

可以借助清单元素中的属性以及您传递给 startActivity() 的 intent 中的标记来实现上述目的。

在这方面,您可以使用的主要 属性包括:

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

您可以使用的主要 intent 标记包括:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

自定义启动模式

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

  • 使用清单文件

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

  • 使用 Intent 标记

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

因此,如果 Activity A 启动 Activity B,Activity B 可在其清单中定义如何与当前任务相关联(如果关联的话),Activity A 也可以请求 Activity B 应该如何与当前任务关联。如果两个 Activity 都定义了 Activity B 应如何与任务关联,将优先遵循 Activity A 的请求(在 intent 中定义),而不是 Activity B 的请求(在清单中定义)。

值得注意的是,有些启动模式仅可以通过intent标记定义,有些仅可以使用清单定义。

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

在清单文件中声明 Activity 时,可以使用元素的 launchMode属性指定 Activity 应该如何与任务关联。

示例代码:

<activity android:name=".MainActivity"
            android:launchMode="modeName">
            ....
</activity>

launchMode属性说明了 Activity 应如何启动到任务中:

  1. “standard”(默认模式)

    系统在启动该 Activity 的任务中创建 Activity 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。

  2. “singleTop”

    如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其 onNewIntent() 方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。

  3. “singleTask”

    系统会创建新任务,并实例化新任务的根 Activity。但是,如果另外的任务中已存在该 Activity 的实例,则系统会通过调用其 onNewIntent() 方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在。

    值的注意的是,虽然新的Activity在新任务中启动,但是用户按返回按钮仍会返回到上一个Activity,即切换了前台Task。

  4. “singleInstance”

    "singleTask" 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

无论 Activity 是在新任务中启动的,还是在和启动它的 Activity 相同的任务中启动,用户按返回按钮都会回到上一个 Activity。但是,如果启动了指定 singleTask 启动模式的 Activity,而后台任务中已存在该 Activity 的实例,则系统会将该后台任务整个转到前台运行。此时,返回堆栈包含了转到前台的任务中的所有 Activity,这些 Activity 都位于堆栈的顶部。

即在声明了"singleTask"的Activity在从后台转往前台时,其所在的任务全部返回到任栈顶部。

示意图:

在这里插入图片描述

模式单词的含义都是以Task做为主语的。

使用Intent标记自定义启动模式

启动 Activity 时,可以在传送给 startActivity() 的 intent 中添加相应的标记来修改 Activity 与其任务的默认关联。

示例代码:

@Override
public void startActivity(Intent intent) {
    super.startActivity(intent);
    intent.setFlags(Intent.FLAG_ACTIVITY_XXXX);
}

可以使用如下标记来修改默认行为:

  1. “FLAG_ACTIVITY_NEW_TASK”

    在新任务中启动 Activity。如果您现在启动的 Activity 已经有任务在运行,则系统会将该任务转到前台并恢复其最后的状态,而 Activity 将在 onNewIntent() 中收到新的 intent。

    与在清单文件中launchMode取值为“singleTask”时行为相同。

  2. “FLAG_ACTIVITY_SINGLE_TOP”

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

    与在清单文件中launchMode取值为“singleTop”时行为相同。

  3. “FLAG_ACTIVITY_CLEAR_TOP”

    如果要启动的 Activity 已经在当前任务中运行,则不会启动该 Activity 的新实例,而是会销毁位于它之上的所有其他 Activity,并通过 onNewIntent() 将此 intent 传送给它的已恢复实例(现在位于堆栈顶部)。

处理亲和性

“亲和性”表示 Activity 倾向于属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此具有亲和性。因此,在默认情况下,同一应用中的所有 Activity 都倾向于位于同一任务。

在不同应用中定义的 Activity 可以具有相同的亲和性,或者在同一应用中定义的 Activity 也可以被指定不同的任务亲和性。

可以使用元素的 taskAffinity属性修改任何给定 Activity 的亲和性。

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

  • 当启动Activity的intent包含FLAG_ACTIVITY_NEW_TASK标记时。

    默认情况下,新 Activity 会启动到调用 startActivity() 的 Activity 的任务中。它会被推送到调用方 Activity 所在的返回堆栈中。但是,如果传递给 startActivity() 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记,则系统会寻找其他任务来容纳新 Activity。通常会是一个新任务,但也可能不是。如果已存在与新 Activity 具有相同亲和性的现有任务,则会将 Activity 启动到该任务中。如果不存在,则会启动一个新任务。

  • 当Activity的allowTaskReparenting属性取值为"true"时。

    在这种情况下,一旦和 Activity 有亲和性的任务进入前台运行,Activity 就可从其启动的任务转移到该任务。

    举例来说,假设一款旅行应用中定义了一个报告特定城市天气状况的 Activity。该 Activity 与同一应用中的其他 Activity 具有相同的亲和性(默认应用亲和性),并通过此属性支持重新归属。当您的某个 Activity 启动该天气预报 Activity 时,该天气预报 Activity 最初会和您的 Activity 同属于一个任务。不过,当旅行应用的任务进入前台运行时,该天气预报 Activity 就会被重新分配给该任务并显示在其中。

清除返回栈

如果用户离开任务较长时间,系统会清除任务中除根 Activity 以外的所有 Activity。当用户再次返回到该任务时,只有根 Activity 会恢复。系统之所以采取这种行为方式是因为,经过一段时间后,用户可能已经放弃了之前执行的操作,现在返回任务是为了开始某项新的操作。

可以使用一些 Activity 属性来修改此行为:

  • alwaysRetainTaskState

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

  • clearTaskOnLaunch

    如果在任务的根 Activity 中将该属性设为 "true",那么只要用户离开任务再返回,堆栈就会被清除到只剩根 Activity。也就是说,它与 alwaysRetainTaskState 正好相反。用户始终会返回到任务的初始状态,即便只是短暂离开任务也是如此。

  • finishOnTaskLaunch

    该属性与 clearTaskOnLaunch类似,但它只会作用于单个 Activity 而非整个任务。它还可导致任何 Activity 消失,包括根 Activity。如果将该属性设为 "true",则 Activity 仅在当前会话中归属于任务。如果用户离开任务再返回,则该任务将不再存在。

启动任务

您可以设置一个 Activity 作为任务的入口点,方法是为该 Activity 提供一个 intent 过滤器,并将 "android.intent.action.MAIN" 作为指定操作,将 "android.intent.category.LAUNCHER" 作为指定类别。

示例代码:

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

第二个作用非常重要:用户必须能够离开任务,之后再使用此 Activity 启动器返回到该任务。因此,只有当 Activity 具有 ACTION_MAINCATEGORY_LAUNCHER 过滤器时,才应使用 "singleTask""singleInstance" 这两种启动模式,它们会将 Activity 标记为始终启动任务。比如,可以想象一下,如果缺少该过滤器会发生什么情况:intent 会启动 "singleTask" Activity,随之启动新任务,用户花了一些时间在该任务上。然后,用户按主屏幕按钮。此时,该任务会转到后台,不再可见。现在,用户无法返回到该任务,因为它未显示在应用启动器中。

发布了26 篇原创文章 · 获赞 8 · 访问量 2084

猜你喜欢

转载自blog.csdn.net/qq_40462579/article/details/104521406