Android官方文档—APP组件(Activities)(Tasks and Back Stack)

任务和回退堆栈

应用程序通常包含多个Activity。每个Activity都应围绕用户可以执行的特定操作进行设计,并可以启动其他Activity。例如,电子邮件应用程序可能有一个Activity来显示新消息列表。当用户选择Activity时,将打开一个新Activity以查看该消息。

Activity甚至可以启动设备上其他应用程序中存在的Activity。例如,如果您的应用程序想要发送电子邮件,您可以定义执行“发送”Action的意图并包含一些数据,例如电子邮件地址和消息。然后打开另一个声明自己处理此类意图的应用程序的Activity。在这种情况下,目的是发送电子邮件,因此电子邮件应用程序的“撰写”Activity启动(如果多个活动支持相同的意图,则系统允许用户选择使用哪个)。发送电子邮件后,您的Activity将resume,似乎电子邮件Activity是您的应用程序的一部分。尽管Activity可能来自不同的应用程序,但Android通过将两个Activity保持在同一任务中来维护这种无缝的用户体验。

任务是用户在执行特定作业时与之交互的Activity的集合。Activity按堆栈(回退堆栈)排列,按每个活动打开的顺序排列。 

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

当前Activity从另一个Activity启动时,新Activity将被推到堆栈顶部并获得焦点。之前的Activity仍在堆栈中,但已停止。当Activity停止时,系统将保留其用户界面的当前状态。当用户按下“返回”按钮时,当前活动将从堆栈顶部弹出(活动被销毁),之前的Activity将恢复(其UI的先前状态将恢复)。堆栈中的Activity永远不会重新排列,只能在当前活动启动时压入到堆栈中,并在用户使用“返回”按钮离开时弹出。因此,回退堆栈作为“后进先出”对象结构。图1显示了这种行为,时间轴显示了Activity之间的转换过程以及每个时间点的当前回退栈。

图1.表示任务中的每个新Activity如何将 其添加到回退堆栈。当用户按下“返回”按钮时,将销毁当前Activity并恢复先前的Activity。

如果用户继续按Back,则弹出堆栈中的每个Activity以显示前一个Activity,直到用户返回主屏幕(或任务开始时运行的任何Activity)。从堆栈中删除所有Activity后,该任务不再存在。

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

任务是一个组合单元,当用户开始新任务或通过主页按钮进入主屏幕时,可以移动到“后台”。在后台,任务中的所有Activity都会停止,但任务的后台堆栈保持不变 - 任务只是失去了焦点,如图2所示。然后任务可以返回到“前台“所以用户可以从中断的地方继续前进。例如,假设当前任务(任务A)在其堆栈中有三个Activity - 在当前Activity之下有两个Activity。用户按下Home按钮,然后从应用程序启动器启动新应用程序。出现主屏幕时,任务A进入后台。当新应用程序启动时,系统会使用自己的一系列Activity为该应用程序(任务B)启动任务。在与该应用程序交互之后,用户再次返回Home并选择最初启动任务A的应用程序。现在,任务A到达前台 - 其堆栈中的所有三个Activity都是完整的,并且堆栈顶部的Activity将恢复。此时,用户还可以通过返回主页并选择启动该任务的应用程序图标(或从概览屏幕中选择应用程序的任务)切换回任务B.这是Android上的多任务处理的一个示例。

注意:可以在后台同时保存多个任务。但是,如果用户同时运行许多后台任务,系统可能会开始销毁后台Activity以恢复内存,从而导致Activity状态丢失。请参阅以下有关Activity状态的部分。

图3.单个Activity多次实例化

由于后备堆栈中的Activity永远不会重新排列,如果您的应用程序允许用户从多个Activity启动特定Activity,则会创建该活动的新实例并将其推送到堆栈(而不是引入任何先前的Activity实例)到顶部)。因此,应用程序中的一个Activity可能会被多次实例化(甚至来自不同的任务),如图3所示。因此,如果用户使用“返回”按钮向后导航,则Activity的每个实例都按顺序显示被打开(每个都有自己的UI状态)。但是,如果您不希望多次实例化Activity,则可以修改此行为。有关如何执行此操作将在后面的“管理任务”一节中讨论。

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

  • 当Activity A启动Activity B时,Activity A停止,但系统保留其状态(例如滚动位置和输入到表单中的文本)。如果用户在Activity B中按下“返回”按钮,则Activity A将恢复其状态。
  • 当用户通过按Home键离开任务时,当前Activity 将停止,其任务将进入后台。系统保留任务中每个活动的状态。如果用户稍后通过启动器图标来恢复任务,则任务将到达前台并恢复堆栈顶部的Activity。
  • 如果用户按下“返回”按钮,则会从堆栈中弹出当前Activity并将其销毁。堆栈中先前的活动被恢复。当活动被销毁时,系统不会保留Activity的状态。
  • Activity可以多次实例化,甚至可以从其他任务实例化。

Navigation Design

For more about how app navigation works on Android, read Android Design's Navigation guide。

保存Activity状态


如上所述,系统的默认行为会在Activity停止时保留Activity的状态。这样,当用户导航回上一个Activity时,其用户界面就会以他们离开时的样子显示。但是,如果Activity被销毁并且必须重新创建,则可以应该主动使用回调方法保留Activity的状态。

当系统停止您的某个Activity时(例如,当新Activity启动或任务移至后台时),如果需要恢复系统内存,系统可能会完全销毁该Activity。发生这种情况时,有关Activity状态的信息将丢失。如果发生这种情况,系统仍然知道Activity在回退堆栈中有一个位置,但是当Activity被带到堆栈顶部时,系统必须重新创建它(而不是恢复它)。为了避免丢失用户的工作,您应该通过在Activity中实现onSaveInstanceState()回调方法来主动保留它。

有关如何保存活动状态的详细信息,请参阅“Activities”文档。

管理任务


如上所述,Android管理任务和后台堆栈的方式 - 将所有Activity连续放入同一任务和“后进先出”堆栈 - 对大多数应用程序都很有用,你不必担心关于您的Activity如何与任务相关联或它们如何存在于后台堆栈中。但是,您可能决定要中断正常行为。也许您希望应用程序中的Activity在启动时开始新任务(而不是放在当前任务中);或者,当你开始一个Activity时,你想要使用它的现有实例(而不是在后面的堆栈顶部创建一个新的实例);或者,您希望在用户离开任务时清除回退堆栈中除了根Activity之外的所有Activity。

您可以使用<activity>清单元素中的属性,以及修改传递给startActivity()的intent中的flag值执行这些操作以及更多操作。

这里,在<activity>中您可以使用的主要属性是:

您可以使用的主要意图flag是:

在以下部分中,您将了解如何使用这些清单属性和意图标志来定义Activity与任务的关联方式以及它们在回退堆栈中的行为方式。

另外,单独讨论的是如何在概览屏幕中表示和管理任务和Activity的注意事项。有关详细信息,请参阅概述屏幕,您应该允许系统在概览屏幕中定义您的任务和Activity的显示方式,而不需要修改这些行为。

警告:大多数应用程序不应该中断Activity和任务的默认行为。如果您确定您的Activity需要修改默认行为,请谨慎使用,并确保在启动期间以及使用“返回”按钮从其他Activity和任务导航回Activity时预测Activity的可用性。请务必检查可能与用户预期行为冲突的导航行为。

启动模式的定义

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

  • 使用清单文件

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

  • 使用意图flag

当您调用startActivity()时,您可以在Intent中包含一个flag,该标志声明新Activity应如何(或是否)与当前任务关联。

因此,如果Activity A启动Activity B,Activity B可以在其清单中定义它应该如何与当前任务相关联(如果有的话),Activity A也可以请求Activity B应该如何与当前任务相关联。如果两个Activity都定义了Activity B应该如何与任务相关联,则遵循活动B的请求(如其清单中所定义)。

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

使用清单文件

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

launchMode属性指定有关如何将Activity 启动到任务的说明。您可以为launchMode属性分配四种不同的启动模式:

"standard" (the default mode)

默认。系统在启动它的任务中创建Activity的新实例,并将意图传递到该实例。Activity可以多次实例化,每个实例可以属于不同的任务,一个任务可以有多个实例。

"singleTop"

如果Activity已存在于当前任务的顶部,则系统通过调用其onNewIntent()方法将意图传递到该实例,而不是创建Activity的新实例。Activity可以多次实例化,每个实例可以属于不同的任务,一个任务可以有多个实例(但只有当回退堆栈顶部的Activity不是Activity的现有实例时)。

例如,假设任务的回退堆栈由根Activity A组成,Activity B,C和D位于顶部(堆栈为A-B-C-D; D位于顶部)。如果当意图传递到D类的Activity。并且D具有默认的“standard”启动模式,则启动该类的新实例并且堆栈变为A-B-C-D-D。但是,如果D的启动模式是“singleTop”,则现有的D实例通过onNewIntent()接收意图,因为它位于堆栈的顶部 - 堆栈仍然是A-B-C-D。但是,如果意图到达B类的Activity,则即使其启动模式为“singleTop”,也会将新的B实例添加到堆栈中。

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

"singleTask"

系统创建新任务并在新任务的根目录下实例化Activity。但是,如果Activity的实例已存在于单独的任务中,则系统会通过调用其onNewIntent()方法将意图路由到现有实例,而不是创建新实例。一次只能存在一个Activity实例。

注意:虽然活动在新任务中启动,但“后退”按钮仍会将用户返回到上一个Activity。

"singleInstance"

与“singleTask”相同,但系统不会在持有实例的任务中启动任何其他Activity。Activity始终是其任务的唯一成员;任何由此开始的Activity都在一个单独的任务中打开。

另一个示例,Android浏览器应用程序声明Web浏览器Activity应始终在其自己的任务中打开 - 通过在<activity>元素中指定singleTask启动模式。这意味着,如果您的应用程序发出打开Android浏览器的意图,则其Activity不会与您的应用程序放在同一任务中。而是要么为浏览器启动新任务,要么如果浏览器已经在后台运行任务,则该任务将被置于前台来处理新意图。

无论Activity是在新任务中启动还是在与启动它的Activity相同的任务中启动,“返回”按钮始终会将用户带到上一个Activity。但是,如果启动指定singleTask启动模式的Activity,则如果后台任务中存在该Activity的实例,则将整个任务带到前台。此时,回退堆栈压入该任务中的所有Activity。图4说明了这种情况。

图4.表示如何将具有启动模式“singleTask”的Activity添加到后台堆栈。如果Activity已经是具有自己的后台堆栈的后台任务的一部分,那么整个后台堆栈放置在当前任务顶端。

有关在清单文件中使用启动模式的详细信息,请参阅<activity>元素文档,其中详细讨论了launchMode属性和可用的值。

注意:您使用launchMode属性为Activity指定的行为可以被包含在启动Activity的intent中的标记覆盖,如下一节中所述。

使用意图flag

启动Activity时,您可以通过在传递给startActivity()的包含flag的意图来修改Activity与其任务的默认关联方式。您可以用来修改默认行为的flag是:

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,并将此意图通过onNewIntent()传递给Activity的resume实例(在回退堆栈的顶部))。

生成此行为的launchMode属性没有任何值。

FLAG_ACTIVITY_CLEAR_TOP通常与FLAG_ACTIVITY_NEW_TASK结合使用。When used together, these flags are a way of locating an existing activity in another task and putting it in a position where it can respond to the intent.

注意:对于FLAG_ACTIVITY_CLEAR_TOP,如果指定Activity的启动模式是“standard”,它也会从堆栈中删除,并在其位置启动新实例以处理传入的意图。这是因为当启动模式为“standard”时,总是为新意图创建新实例。

关联性处理

关联性指Activity希望属于哪个任务。默认情况下,同一应用程序中的所有Activity都具有彼此的关联。因此,默认情况下,同一应用程序中的所有Activity都希望处于同一任务中。但是,您可以修改Activity的默认关联。在不同应用程序中定义的Activity可以共享关联性,或者可以为同一应用程序中定义的Activity分配不同的任务关联性。

您可以使用<activity>元素的taskAffinity属性修改任何给定Activity的关联性。

taskAffinity属性采用字符串值,该值必须与<manifest>元素中声明的默认包名称唯一。因为系统使用该名称来标识应用程序的默认任务关联。

关联性在两种情况下起作用:

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

默认情况下,新Activity将启动到调用startActivity()的Activity的任务中。它被调到与调用者相同的回退栈。但是,如果传递给startActivity()的intent包含FLAG_ACTIVITY_NEW_TASK标志,则系统会查找另一个任务以容纳新Activity。通常,这是一项新任务。但是,它不一定是。如果已存在与新Activity具有相同关联性的现有任务,则会将活动启动到该任务中。如果没有,它开始一项新任务。

如果此标志导致Activity开始新任务,并且用户按下主页按钮以离开它,则必须有某种方式让用户导航回任务。某些实体(例如通知管理器)总是在外部任务中启动Activity,从不作为自己的一部分,因此它们总是将FLAG_ACTIVITY_NEW_TASK置于它们传递给startActivity()的意图中。如果您有可以由可能使用此标志的外部实体调用的Activity,请注意用户有一种独立的方式返回已启动的任务,例如使用启动器图标(任务的根Activity)有一个CATEGORY_LAUNCHER意图过滤器;请参阅下面的“启动任务”部分。

  • 当一个活动的allowTaskReparenting属性设置为“true”时。

在这种情况下,当该任务到达前台时,Activity可以从它开始的任务移动到它具有亲和力的任务。

例如,假设在所选城市中报告天气状况的Activity被定义为旅行应用程序的一部分。它与同一应用程序中的其他Activity(默认应用程序关联)具有相同的关联,并允许使用此属性重新生成父项。当您的某个Activity启动天气报告者Activity时,它最初属于与您的Activity相同的任务。但是,当旅行应用程序的任务到达前台时,天气报告者Activity将重新分配给该任务并显示在其中。

提示:如果.apk文件从用户的角度包含多个“应用程序”,您可能希望使用taskAffinity属性为与每个“应用程序”关联的活动分配不同的关联。

清理回退栈

如果用户长时间离开任务,系统将清除任务中除根活动之外的所有Activity。当用户再次返回任务时,仅还原根活动。系统以这种方式运行,因为在很长一段时间之后,用户可能已经放弃了之前正在做的事情并返回任务以开始新的事情。

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

alwaysRetainTaskState

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

clearTaskOnLaunch

如果在任务的根Activity中将此属性设置为“true”,则只要用户离开任务并返回到该任务,就会将堆栈清除为根Activity。换句话说,它与alwaysRetainTaskState相反。即使在离开任务片刻之后,用户也始终以初始状态返回任务。

finishOnTaskLaunch

此属性类似于clearTaskOnLaunch,但它在单个Activity上运行,而不是整个任务。它还可以导致任何Activity消失,包括根Activity。当它设置为“true”时,Activity仍然是当前会话的任务的一部分。如果用户离开然后返回任务,它将不再存在。

启动任务

您可以将活动设置为Activity的入口点,方法是为其指定一个过滤器,其中“android.intent.action.MAIN”作为指定的action,“android.intent.category.LAUNCHER”作为指定的类别。例如:

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

这种意图过滤器会导致Activity的图标和标签显示在应用程序启动器中,从而为用户提供启动Activity的方法,并在启动后随时返回创建的任务。 

第二种能力很重要:用户必须能够离开任务,然后使用此Activity启动器返回该任务。因此,仅当Activity具有ACTION_MAIN和CATEGORY_LAUNCHER过滤器时,才应使用将Activity标记为始终启动任务的两种启动模式“singleTask”和“singleInstance”。例如,想象一下,如果缺少过滤器会发生什么:intent会启动“singleTask”Activity,启动新任务,并且用户会花一些时间在该任务中工作。然后用户按下主页按钮。该任务现在发送到后台并且不可见。现在用户无法返回任务,因为它未在应用程序启动器中显示。

对于您不希望用户能够返回Activity的情况,请设置<activity> 元素的finishOnTaskLaunch为“true”(请参阅​​清除堆栈)。

有关如何在概览屏幕中表示和管理任务和活动的更多信息,请参阅概述屏幕。

猜你喜欢

转载自blog.csdn.net/weixin_42703445/article/details/82908765
今日推荐