理解任务和返回堆栈
任务是用户在执行某项工作时与之交互的一系列活动。这些活动排列在一个堆栈中 - 后台堆栈) - 按每个活动的打开顺序排列。例如,电子邮件应用程序可能有一个活动来显示新消息列表。当用户选择一条消息时,将打开一个新的活动来查看该消息。这个新活动被添加到后退堆栈中。如果用户按下“ 后退”按钮,则新活动结束并弹出堆栈。以下视频很好地概述了背部堆叠的工作原理。
当应用程序在多窗口环境中同时运行时 ,在Android 7.0(API级别24)及更高版本中受支持,系统将为每个窗口分别管理任务; 每个窗口可能有多个任务。对于 在Chromebook上运行的Android应用程序也是如此:系统以每个窗口为基础管理任务或任务组。
设备主屏幕是大多数任务的起始位置。当用户触摸应用程序启动器中的图标(或主屏幕上的快捷键)时,该应用程序的任务将显示在前台。如果应用程序没有任何任务(应用程序最近未使用),则会创建一个新任务,并且该应用程序的“主要”活动作为堆栈中的根活动打开。
当当前活动开始另一个时,新活动被推到堆栈的顶部并且获得焦点。先前的活动保留在堆栈中,但已停止。当活动停止时,系统保留其用户界面的当前状态。当用户按下“ 后退” 按钮时,当前活动从堆栈顶部弹出(活动被销毁),并恢复前一活动(恢复其UI的以前状态)。堆栈中的活动不会重新排列,只能从堆栈推送并弹出 - 在当前活动启动时推送到堆栈,并在用户使用Back时离开堆栈时弹出按钮。因此,后堆栈作为“后进先出”对象结构运行。图1通过一个时间线将这种行为可视化,该时间表显示活动之间的进展情况以及每个时间点当前的后退堆栈。
图1.任务中每个新活动如何将项添加到后端堆栈的表示。当用户按下“ 后退”按钮时,当前活动将被销毁并恢复前一活动。
如果用户继续按回,则弹出堆栈中的每个活动以显示前一个活动,直到用户返回主屏幕(或任务开始时的任何活动正在运行)。当所有活动从堆栈中移除时,任务不再存在。
图2.两个任务:任务B在前台接收用户交互,而任务A在后台,等待恢复。
图3.单个活动被多次实例化。
任务是一个内聚单元,当用户开始新任务或通过主页按钮进入主屏幕时,该单元可以移动到“背景” 。在后台,任务中的所有活动都会停止,但任务的后退堆栈保持不变 - 任务完全失去焦点,而发生另一项任务,如图2所示。然后,任务可以返回到“前景“,所以用户可以从他们离开的地方拿起。例如,假设当前任务(任务A)在其当前活动下的栈中有三个活动。用户按下主页 按钮,然后从应用启动器启动一个新应用。主屏幕出现时,任务A进入后台。当新应用程序启动时,系统会使用自己的一系列活动为该应用程序启动一项任务(任务B)。与该应用程序交互之后,用户再次返回Home并选择最初启动Task A的应用程序。现在,Task A进入前台 - 堆栈中的所有三个活动都保持不变,并且堆栈顶部的活动恢复。在这一点上,用户还可以通过回家并选择启动该任务的应用程序图标(或从选择应用程序的任务切换到任务B 最近通话屏幕)。这是Android上多任务的一个例子。
注意:多个任务可以一次在背景中进行。但是,如果用户同时运行多个后台任务,系统可能会开始销毁后台活动以恢复内存,从而导致活动状态丢失
因为后堆栈中的活动从不重新排列,所以如果您的应用允许用户从多个活动中启动特定活动,则会创建该活动的新实例并将其推入堆栈(而不是将任何先前的活动实例到顶部)。因此,您的应用程序中的一项活动可能会多次实例化(即使是来自不同的任务),如图3所示。因此,如果用户使用“ 后退”按钮向后导航,则活动的每个实例按照它们的顺序显示被打开(每个都有自己的UI状态)。但是,如果您不希望将活动实例化多次,则可以修改此行为。在后面的“ 管理任务”一节中讨论了如何实现。
总结活动和任务的默认行为:
- 当活动A开始活动B时,活动A停止,但系统保留其状态(例如滚动位置和输入到表单中的文本)。如果用户在活动B中按下后退按钮,则活动A将恢复其状态并恢复。
- 当用户通过按Home按钮离开任务时,当前活动停止并且其任务进入后台。系统保留任务中每个活动的状态。如果用户稍后通过选择启动该任务的启动器图标来恢复该任务,则该任务将进入前台并恢复堆栈顶部的活动。
- 如果用户按下“ 后退”按钮,则当前活动从堆栈中弹出并销毁。堆栈中的前一个活动恢复。当活动被销毁时,系统 不保留活动的状态。
- 活动可以多次实例化,甚至可以从其他任务中实例化。
导航设计
有关Android应用导航如何工作的更多信息,请阅读Android设计的导航指南。
管理任务
如上所述,Android管理任务和后退堆栈的方式 - 通过将所有活动连续开始放置在同一个任务中以及“后进先出”堆栈中 - 对大多数应用程序非常有用,您不必担心了解您的活动如何与任务相关联以及它们如何在后端堆栈中存在。但是,您可能会决定要中断正常行为。也许你希望应用中的某个活动在启动时开始新任务(而不是放置在当前任务中); 或者,当你开始一个活动时,你想要提出它的一个现有的实例(而不是在后面的栈上创建一个新的实例); 或者,您希望在用户离开任务时清除您的后退堆栈中除根活动外的所有活动。
您可以通过<activity> 清单元素中的属性 以及您传递给目标的标志来执行以上操作 startActivity()。
在这方面,你可以使用的主要属性是: <activity>
- taskAffinity
- launchMode
- allowTaskReparenting
- clearTaskOnLaunch
- alwaysRetainTaskState
- finishOnTaskLaunch
而您可以使用的主要意图标志是:
- FLAG_ACTIVITY_NEW_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_SINGLE_TOP
在以下各节中,您将了解如何使用这些清单属性和意向标志来定义活动与任务的关联方式以及它们在后退堆栈中的行为方式。
另外,分别讨论了如何在“ 最近”屏幕中表示和管理任务和活动。请参阅最近屏幕 了解更多信息。通常情况下,您应允许系统定义您的任务和活动在最近屏幕中的表现方式,而不需要修改此行为。
警告:大多数应用程序不应该中断活动和任务的默认行为。如果您确定自己的活动需要修改默认行为,请谨慎使用,并务必在启动过程中以及使用“ 后退”按钮从其他活动和任务导航回来时测试活动的可用性。务必测试可能与用户预期行为相冲突的导航行为。
定义启动模式
启动模式允许您定义活动的新实例如何与当前任务相关联。您可以通过两种方式定义不同的启动模式:
-
使用清单文件
当您在清单文件中声明活动时,您可以指定活动在启动时应如何与任务相关联。
- 使用意图标志
当你打电话时startActivity(),你可以在其中包含一个标志Intent,声明新活动应该如何(或是否)与当前任务相关联。
因此,如果活动A启动活动B,活动B可以在其清单中定义应该如何与当前任务相关联(如果有的话),并且活动A还可以请求活动B如何与当前任务相关联。如果两项活动都定义了活动B如何与任务相关联,那么活动A的请求(如意图中定义的)将根据活动B的请求(如其清单中所定义的)得到兑现。
注意:可用于清单文件的某些启动模式不可用作意图的标志,同样,某些启动模式可用作意图的标志,因此无法在清单中定义。
使用清单文件
在清单文件中声明活动时,可以指定活动如何与使用<activity> 元素launchMode属性的任务相关联。
该launchMode属性指定了如何将活动发送到任务中的指令。有四种不同的启动模式可以分配给 launchMode 属性:
"standard" (默认模式)
默认。系统在启动它的任务中创建活动的新实例,并将意图传递给它。该活动可以多次实例化,每个实例可以属于不同的任务,一个任务可以有多个实例。
"singleTop"
如果活动的实例已经存在于当前任务的顶部,则系统通过对其onNewIntent()方法的调用将意图路由到该实例,而不是创建活动的新实例。该活动可以多次实例化,每个实例可以属于不同的任务,并且一个任务可以具有多个实例(但只有当后栈的顶部的活动不是活动的现有实例时)。
例如,假设一个任务的返回堆栈由根活动A和顶部的活动B,C和D组成(堆栈是ABCD; D在顶部)。意图到达类型D的活动。如果D具有默认"standard"启动模式,则启动该类的新实例,堆栈变为ABCDD。但是,如果D的启动模式是"singleTop",则D的现有实例将接收到意图onNewIntent(),因为它位于堆栈的顶部 - 堆栈保留为ABCD。但是,如果意图到达类型B的活动,那么即使启动模式为,B的新实例也会添加到堆栈中"singleTop"。
注意:创建活动的新实例时,用户可以按“ 后退”按钮返回到上一个活动。但是,当活动的现有实例处理新的意图时,在新意图抵达之前,用户无法按下后退按钮返回到活动状态onNewIntent()。
"singleTask"
系统创建一个新任务并在新任务的根目录下实例化活动。但是,如果活动的实例已经存在于单独的任务中,则系统通过调用其onNewIntent()方法来将意图路由到现有实例 ,而不是创建新实例。一次只能有一个活动实例。
注意:尽管活动在新任务中开始,但 后退按钮仍然会将用户返回到上一个活动。
"singleInstance"
与此相同"singleTask",只是系统不会将任何其他活动发送到持有该实例的任务中。该活动始终是其任务中唯一且唯一的成员; 任何由此人开始的活动都将在单独的任务中打开。
作为另一个例子,Android浏览器应用程序声明Web浏览器活动应始终在其自己的任务中打开 - 通过singleTask在<activity>元素中指定启动模式。这意味着如果您的应用发布了打开Android浏览器的意图,则其活动不会与您的应用放在同一个任务中。相反,为浏览器启动一个新任务,或者如果浏览器已经有一个任务在后台运行,那么该任务将被提出来处理新的意图。
无论某个活动是在新任务中启动,还是在与启动该活动的活动相同的任务中启动,“ 后退”按钮始终将用户引导至之前的活动。但是,如果启动一个指定singleTask启动模式的活动,那么如果该活动的一个实例存在于后台任务中,则该整个任务将被带到前台。此时,后台堆栈现在包含来自任务的所有活动,位于堆栈顶部。图4说明了这种情况。
图4.如何将启动模式为“singleTask”的活动添加到后端堆栈中。如果活动已经是具有自己的后退堆栈的后台任务的一部分,则整个后退堆栈也会出现在当前任务的顶部。
有关在清单文件中使用启动模式的更多信息,请参阅 <activity> 元素文档,其中launchMode更多地讨论了属性和接受的值。
注意:您使用该launchMode属性为您的活动指定的行为可以被包含在开始您的活动的意图中的标志覆盖,如下一节所述。
使用意图标志
在开始活动时,您可以通过在您提供的意向中包含标志来修改活动与其任务的默认关联startActivity()。您可以用来修改默认行为的标志是
FLAG_ACTIVITY_NEW_TASK
在新任务中启动活动。如果某个任务已经开始执行您正在开始的活动,那么该任务将被恢复到前台状态,同时恢复其最后一个状态,并且该活动将接收新的意图onNewIntent()。
这会产生与"singleTask" launchMode上一节中讨论的值相同的行为。
FLAG_ACTIVITY_SINGLE_TOP
如果正在启动的活动是当前活动(位于后端堆栈的顶部),则现有实例将接收到调用onNewIntent(),而不是创建活动的新实例。
这会产生与"singleTop" launchMode上一节中讨论的值相同的行为。
FLAG_ACTIVITY_CLEAR_TOP
如果正在启动的活动已经在当前任务中运行,那么不是启动该活动的新实例,而是将其上的所有其他活动销毁,并将此意向传递给活动的恢复实例(现在开启顶部),通过onNewIntent())。
没有launchMode 产生此行为的属性的值。
FLAG_ACTIVITY_CLEAR_TOP最经常与之配合使用 FLAG_ACTIVITY_NEW_TASK。当一起使用时,这些标志是一种在另一项任务中查找现有活动并将其放置在可以响应意图的位置的方法。
注意:如果指定活动的启动模式是 "standard",它也会从堆栈中移除,并在其位置启动一个新实例来处理传入的意图。这是因为在启动模式为时,总是为新的意图创建新实例"standard"
处理亲和力
该亲和力表示活动更喜欢哪个任务属于。默认情况下,来自同一应用的所有活动都具有相互关联性。因此,默认情况下,同一应用中的所有活动都喜欢处于相同的任务中。但是,您可以修改某个活动的默认关联。在不同应用中定义的活动可以共享亲缘关系,或者可以为在同一应用中定义的活动分配不同的任务亲缘关系。
您可以使用 元素的taskAffinity属性修改任何给定活动的关联性<activity>。
该taskAffinity 属性接受一个字符串值,该值必须与元素中声明的默认包名称相同,因为系统使用该名称来标识该应用程序的默认任务关联。 <manifest>
亲和力在两种情况下发挥作用:
-
当启动活动的意图包含 FLAG_ACTIVITY_NEW_TASK 标志时。
默认情况下,新活动将启动到所调用活动的任务中startActivity()。与主叫方一样,它被推送到相同的后端堆栈。但是,如果传递的意图 startActivity() 包含FLAG_ACTIVITY_NEW_TASK 标志,则系统会寻找不同的任务来保存新活动。通常,这是一项新任务。但是,它不一定是。如果已有与新活动具有相同亲和力的现有任务,则该活动将启动到该任务中。如果不是,它开始一个新的任务。
如果此标志导致一个活动开始一项新任务,并且用户按下主页按钮离开它,那么用户必须有某种方式才能导航回任务。一些实体(例如通知管理器)总是在外部任务中开始活动,而不是他们自己的一部分,因此他们总是将FLAG_ACTIVITY_NEW_TASK意图传递给他们 startActivity()。如果您有一个可以使用此标志的外部实体可以调用的活动,请注意用户可以采用独立方式返回已启动的任务,例如使用启动器图标(任务的根活动有一个CATEGORY_LAUNCHER意图过滤器;请参阅下面的启动任务部分)。
-
活动的属性设置为。 allowTaskReparenting"true"
在这种情况下,当任务进入前台时,活动可以从开始的任务移动到与其具有关联性的任务。
例如,假设报告所选城市中的天气状况的活动被定义为旅行应用程序的一部分。它与同一应用程序中的其他活动(默认应用程序关联)具有相同的亲和力,并且允许使用此属性重新进行亲子关系。当您的一项活动启动天气记者活动时,它最初属于与您的活动相同的任务。但是,当旅行应用程序的任务出现在前台时,天气记者活动将重新分配给该任务并显示在其中。
提示:如果APK文件包含用户角度的多个“应用”,则可能希望使用该taskAffinity 属性为与每个“应用”关联的活动分配不同的亲和性。
清理后端堆栈
如果用户长时间离开任务,系统将清除除根活动外的所有活动的任务。当用户再次返回任务时,只有根活动恢复。系统的行为方式是这样的,因为在很长一段时间之后,用户可能已经放弃了之前正在做的事情,并且正在返回到开始新事物的任务。
有一些活动属性可用于修改此行为:
alwaysRetainTaskState
如果此属性设置为"true"任务的根活动,则刚刚描述的默认行为不会发生。即使经过很长时间,任务仍会保留其堆栈中的所有活动。
clearTaskOnLaunch
如果"true"在任务的根活动中将此属性设置为,则每当用户离开任务并返回到该活动时,该堆栈将被清除为根活动。换句话说,这是相反的 。用户总是返回到初始状态的任务,即使在离开任务一段时间之后。 alwaysRetainTaskState
finishOnTaskLaunch
这个属性就像clearTaskOnLaunch,但它在一个活动上运行,而不是整个任务。它也可能导致任何活动消失,包括根活动。当它设置为时"true",活动仅保留当前会话的一部分任务。如果用户离开并返回到任务,则不再存在。
开始一项任务
您可以设置一个活动作为任务的入口点,方法是给它一个意向过滤器, "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>
这种意图过滤器会导致活动的图标和标签显示在应用启动器中,为用户提供一种启动活动并返回其启动后任何时间创建的任务的方式。
第二种能力很重要:用户必须能够离开任务,然后使用此活动启动器稍后再回来。为此,标志着活动一如既往启动任务,这两种启动模式"singleTask"和 "singleInstance",应使用只有当活动有一个 ACTION_MAIN 和CATEGORY_LAUNCHER过滤器。想象一下,例如,如果过滤器丢失会发生什么情况:意图启动一个"singleTask"活动,启动一项新任务,并且用户花费一些时间在该任务中工作。用户然后按下主页 按钮。该任务现在发送到后台并且不可见。现在,用户无法返回任务,因为它未在应用启动器中显示。
对于不希望用户能够返回活动的情况,请将<activity> 元素 设置 finishOnTaskLaunch 为"true"(请参阅清除后退堆栈)。
“ 最近”屏幕中提供有关如何在“ 概览”屏幕中表示和管理任务和活动的更多信息。
更多资源
Android设计:导航
<activity> 清单元素
最近的屏幕
Android方式的多任务处理
Lastest Update:2018.05.03
联系我
QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ
公众号推荐:
转载于:https://blog.51cto.com/4789781/2122333