【Android】任务和返回栈(tasks and back stack)
一个Task 就是一组activity的集合。这些activity按照它们打开的顺序被放置于一个先进后出的栈中(back stack)。
用户点击图标打开一个app时,该app的task会被移到前台显示。如果当前没有该app的task,系统将会新建一个task并在其中运行Main activity。
一个activity(A)打开另一个activity(B),B将会被置于栈顶并显示,A仍然处于栈中,系统会保存它的状态。 如果按下返回键,当前的activity将被弹出栈并destroy掉,前一个activity被resume并重新显示(还原stop前的UI显示)。
下图表示2个activity切换过程该栈的状态。
用户可以通过返回键令task返回栈中的activity依次弹出,当最后一个activity也被弹出后,该task便不再存在。
如果HOME键被按下,从当前app回到桌面,该app的Task会被移到后台,后台的task所属的所有activity都是stop状态,且back stack依然存在——这个task其实只是失去了和用户交互的焦点。
多个task可以同时存在后台,但是系统也会停止一些activity来释放空间,导致activity的状态丢失。
Back stack中的activity不会被重新排位,如果同一个activity能被其他多个activity 启动,这个activity都会创建新实例推入栈中:
总结Activity和task的默认行为:
- Activity A 启动了 Activity B , A 会被 stop ,但状态仍然保存( UI ,如活动条的位置, EditText 输入的文字),从 B 返回, A 被 resumed 并恢复之前的状态。
- 点击 HOME 返回桌面,当前的 task 被移到后台,系统会保存 task 中每一个 activity 的状态;直到用户点击 app 图标 返回,该 task 被移回前台, resume 栈顶的 activity 。
- 返回键会导致当前的 activity 从栈顶弹出并被 destroy ,该 activity 状态不会再被保存,前一个 activity移到栈顶。
- Activity 能被实例化多次,包括其它 task 启动它。
Android管理task和back stack的默认行为:activity 在同一个任务中创建并置于先进后出的栈中。如果这种默认的行为不能满足我们的app设计,如:为一个activity创建一个新任务(而不是在相同的任务中),或者启动activity时直接打开已存在的实例(而不是直接在栈顶创建新实例),又或者在用户离开这个task的时候清空除了栈顶以外的全部activity。Android提供了一些属性和flag让coder来指定管理的方式。
[url=]在manifest <activity>标签中的相关属性:[/url]
[url=]launchMode[/url]
[url=]allowTaskReparenting[/url]
[url=]clearTaskOnLaunch[/url]
[url=]alwaysRetainTaskState[/url]
[url=]finishOnTaskLaunch[/url]
Intent 也有相关的flag:
[url=]FLAG_ACTIVITY_NEW_TASK[/url]
[url=]FLAG_ACTIVITY_CLEAR_TOP[/url]
[url=]FLAG_ACTIVITY_SINGLE_TOP[/url]
定义Launch Mode
Use Cases
|
Launch Mode
|
Multiple Instances?
|
Comments
|
Normal launches for most activities | "standard" | Yes |
默认行为。每次启动一个activity,系统都会在目标task新建一个实例。
|
Normal launches for most activities | "singleTop" | Conditionally |
如果目标activity的实例已经存在于目标task的栈顶,系统会直接使用该实例,并调用该activity的[url=]onNewIntent()[/url](不会重新create)
|
Specialized launches (not recommended for general use) | "singleTask" | No |
在一个新任务的栈顶创建activity的实例。如果实例已经存在,系统会直接使用该实例,并调用该activity的[url=]onNewIntent()[/url](不会重新create)
|
Specialized launches (not recommended for general use) | "singleInstance" | No |
和"singleTask"类似,但在目标activity的task中不会再运行其他的activity,在那个task中永远只有一个activity。
|
SingleTask的例子:浏览器的browser activity设置了SingleTask只运行在它自己的task中,如果Browser的task现在正在后台当中(task B),而我们的app(task A)的正要打开这个activity,这个task就会被直接移到前台接收我们的intent。
返回键只会将界面返回到当前task的下一个activity,所以Task B回到前台后,返回键会先作用在Task B中,直到最后一个activity被弹出,才会回到我们的Task A栈顶的activity。
注意:launchMode能被Intent 的flag覆盖。
使用Intent 标志
FLAG_ACTIVITY_NEW_TASK: 等同于 singleTask
FLAG_ACTIVITY_SINGLE_TOP: 等同singleTop
FLAG_ACTIVITY_CLEAR_TOP: 如果该activity已经运行在当前task中,intent指定启动这个activity时,task中在它上面的activity都会被destroy,直到指定的activity位于栈顶,然后它的onNewIntent()被调用。
Affinity
affinity用于指定activity所属的task。默认状态下,一个app中的所有activity都有相同的affinity,所以它们会运行在同一个task。而通过<activity>的taskAffinity属性可以指定affinity。
taskAffinity要用<manifest>中定义的唯一包名来取值,系统通过包名定位到app的默认task。
taskAffinity在以下2种情况中发生作用:
- 使用FLAG_ACTIVITY_NEW_TASK启动一个activity。如果该activity指定了taskAffinity,系统会将activity实例置于指定的task中。 注意的是,此情况下如果用户点击HOME键,必须要确定有办法能回到那个task中!(例如task所属的app在launcher有自己icon)
- activity 设置了 allowTaskReparenting = “true”。 当activity所在的task被移到前台时,该activity会被移动到affinity指定的task中。
如果用户离开一个task很长时间,系统会清理栈顶以下的activity,这样task被从新打开时,栈顶activity就被还原了。coder同样可以通过<activity>属性改变这种行为:
alwaysRetatinTaskState: 如果当前栈顶的activity设置此属性为true,task中的所有activity都会被保留状态。
clearTaskOnLaunch:如果当前栈顶的activity设置此属性为true,行为则与alwaysRetatinTaskState相反,每次离开并重新该task,栈顶下的所有activity都会被清除,用户返回task时永远都是activity初始化的状态。
finishOnTaskLaunch: 与clearTaskOnLaunch 相似,不过只作用于单个activity,不影响整个task。即使是栈顶的activity,也会生效。