Activity的启动模式和任务栈
简介
上一篇文章详细介绍了Activity生命周期详解及注意事项。根据上一篇文章我们知道,当Activity A开启一个Activity B之后,系统会创建一个Activity B实例,推入到任务栈中,并且位于栈顶,位于Activity A的实例之上;当点击back键之后,Activity B从任务栈中被弹出即Activity B被销毁;Activity A重新可见,再次回到栈顶。
任务栈的工作原理
任务栈中的Activity永远不会重新排列,仅推入和弹出任务栈:Activity启动时被推入到任务栈;用户点击back键,当前Activity被弹出任务栈。所以,任务栈以“后进先出”(FILO)对象结构运行。
上图:显示任务中的每个新Activity如何向任务栈添加项目。用户按Back按钮时,当前Activity随即被销毁,而前一个Activity 恢复执行。
如果用户继续按Back键,任务栈中的相应Activity就会弹出,以显示前一个Activity,当所有Activity均从任务栈中移除后,任务即不复存在。
Activity 和任务的默认行为总结如下:
当 Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本等相关信息)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。
用户通过按Home键离开任务时,当前 Activity 将停止且其任务会进入后台。 系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。
如果用户按Back键,则当前 Activity 会从堆栈弹出并被销毁。 堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统不会保留该 Activity 的状态。
启动模式
正常情况下,每次启动一个Activity都会在任务栈中加入对应的Activity实例,但是有没有特殊情况下,开启Activity不会再次创建同一个Activity实例呢?
现在我们来详细介绍下Activity的四种启动模式,分别为standard,singleTop,singleTask,singleInstance四种模式;
standard:
默认模式。每次启动该Activity都会创建一个实例,添加到任务栈中,不管任务栈中是否已经存在该Activity实例。singleTop
singleTops是栈顶只存在一个该Activity实例
分为两种情况:
1 如果当前任务栈的顶部已存在该Activity的实例,则系统会通过调用该实例的onNewIntent()方法向其传送Intent,而不是创建Activity的新实例;
2 如果当前任务栈的顶部不存在该Activity的实例,和standard模式一样创建一个该Activity的实例,并推入到栈顶。
例如:Activity C的launchMode =“singleTop”;假如当前任务栈(A-B—C),A位于根,C位于栈顶,再次启动Activity C,则Activity C的现有实例会通过onNewIntent()接收Intent,因为它位于任务栈的顶部,不在会创建Actiity C的实例;
如果当前任务栈(A-C-B),B位于栈顶,再次启动Activity C会创建Activity C的实例,并推入到任务栈。任务栈的实例顺序为A—C-B-C;
- singleTask
singleTask是该任务栈只存在一个该Activity实例,属于单例模式;
和singleTop类似也是分为两种情况;
1 当前任务栈中已经存在该Activity的实例的情况下,系统会通过该实例的onNewIntent()方法向其传送Intent,不在创建Activity的新实例;此时又分为两种情况
1.1 如果当前Activity的实例位于栈顶,则和singleTop一样,不再有其他操作;
1.2 如果当前Activity的实例不在栈顶的情况下,会将该实例上面的其他Activity实例全部弹出当前任务栈,该Activity的实例位于栈顶;
因此singleTask模式具有clearTop的作用
2 当前任务栈中不存在该Activity的实例时,和standard模式一样创建一个该Activity的实例,并推入到栈顶;
例如:Activity C的launchMode =“singleTask”;假如当前任务栈(A-B—C),A位于根,C位于栈顶,再次启动Activity C,则Activity C的现有实例会通过onNewIntent()接收Intent,因为它位于任务栈的顶部,不在会创建Actiity C的实例;
如果当前任务栈(A-C-B),B位于栈顶,再次启动Activity C系统会Activity B的实例弹出任务栈,将Activity C的实例置于栈顶,任务栈的实例顺序为A—C;
当前任务栈(A-B),没有Activity C的实例,启动Activity C系统会创建C的实例,并弹入任务栈的顶部,任务栈的实例顺序为A-B-C;
- singleInstance 和singleTask类似,也是属于单例模式,不同点在于singleInstance模式的Activity启动会单独存在于一个任务栈中,并且该任务栈中有且只有这一个Activity实例,不和其他activity共存在一个任务栈。如果多个不同Activity的launchMode采用singleInstance启动模式,系统会为每一个Activity创建一个任务栈,将Activity实例推入到对应的任务栈中。
先在有四个Activity分别为MainActivity,SecondActivityThridActivity,FourthActivity;其中MainActivity和ThridActivity的launchMode为standard;SecondActivity和FourthActivity的launchMode为singleInstance,依次把四个Activity启动。
通过adb shell dumpsys activity activities 命令行查看当前应用的任务栈,会发现
上图 #10064表示任务栈Id,sz:任务栈类的Activity实例数量
从上图可以看出,MainActivity和ThridActivity在同一个任务栈中,SecondActivity和FourthActivity分别位于不同的任务栈;
Intent标志
启动 Activity 时,您可以通过在传递给startActivity()的Intent中加入相应的标志,修改 Activity 与其任务的默认关联方式。可用于修改默认行为的标志包括:
FLAG_ACTIVITY_NEW_TASK在新任务中启动 Activity。如果已为正在启动的 Activity 运行任务,则该任务会转到前台并恢复其最后状态,同时 Activity 会在 onNewIntent() 中收到新 Intent。正如前文所述,这会产生与 “singleTask”launchMode 值相同的行为。
FLAG_ACTIVITY_SINGLE_TOP如果正在启动的Activity是当前Activity(位于返回栈的顶部),则 现有实例会接收对 onNewIntent() 的调用,而不是创建Activity的新实例。
正如前文所述,这会产生与”singleTop”launchMode值相同的行为。FLAG_ACTIVITY_CLEAR_TOP
如果正在启动的 Activity已在当前任务中运行,则会销毁当前任务顶部的所有Activity,并通过 onNewIntent()将此Intent传递给Activity已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。产生这种行为的launchMode属性没有值。
LAG_ACTIVITY_CLEAR_TOP通常与FLAG_ACTIVITY_NEW_TASK结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent的位置。
通过以下两种方式使用Intent Flags
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
addFlags:添加额外的Flags到Intent中;add相当于添加另外一种情况;
setFlags: 设置Flags来控制Intent的处理;set相当于替换之前的Flags;
taskAffinity
taskAffinity可以翻译为任务相关性,关联性;
“关联”指示Activity优先属于哪个任务。默认情况下,同一应用中的所有Activity彼此关联。因此,默认情况下,同一应用中的所有Activity优先位于相同任务中,也可以修改Activity的默认关联。taskAffinity属性取字符串值,默认情况下为应用的包名;但是也可以给Activity指定任务关联,通过taskAffinity设置任务关联的名称,可以为任意字符串,是必须至少包含一个”.”,否则会报错;
在两种情况下,关联会起作用:
- 启动 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 标志。
android:launchMode="singleTask"
android:taskAffinity="frameworkdemo.test"
通过上述方法,可以将Activity放到单独的任务栈中,任务栈的名称为taskAffinity指定;
将SecondActivity设置为android:launchMode=”singleTask”android:taskAffinity=”com.good.things”FourthActivity设置android:launchMode=”singleTask”android:taskAffinity=”frameworkdemo.test”,现在来看下结果:
上图中椭圆的内容表示任务栈Id,矩形表示任务栈的名称,即taskAffinity的指定名称;
- Activity将其allowTaskReparenting属性设置为 “true”。
allowTaskReparenting用来标记Activity能否从启动的Task移动到taskAffinity指定的Task,当把Activity的allowTaskReparenting属性设置成true时,Activity就拥有了一个转移所在Task的能力。具体点来说,就是一个Activity现在是处于某个Task当中的,但是它与另外一个Task具有相同的affinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。allowTaskReparenting默认是继承至application中的allowTaskReparenting=false,如果为true,则表示可以更换;false表示不可以。
例如:ThridActivity设置为 android:allowTaskReparenting=”true”,android:taskAffinity=”allowTaskReparenting.test”,SecondActivity设置为 android:launchMode=”singleTask”,android:taskAffinity=”com.good.things”,现在SecondActivity开启ThridActivity;看下结果:
由上图可知,ThridActivity转移了Task;
思考
如果有两个任务栈A,B;A中有两个Activity,FirstActi
vity,SecondActivity,firstActivity-secondActivity(栈顶),并且lauchMode都是singleTask,B中有thridActiivty,现在思考下1) thridActivity开启SecondActivity之后,依次回退,先后顺序?2)thridActivity开启FirstActivity之后,依次回退,先后顺序?
- 问题一:SecondActivity – FirstActivity – ThridActivity;
- 问题二:FirstActivity – ThridActivity;因为single
参考文档
Google开发文档
任玉刚《Android开发艺术探讨》
上一篇: Android 和 Fragment 生命周期回调执行顺序
下一篇: Activity生命周期时序图
推荐阅读
-
Activity的启动模式
-
Acticity的生命周期和启动模式
-
Unary模式下客户端创建 default-executor 和 resolver-executor 线程和从启动到执行grpc_connector_connect的主要流程
-
一、Activity的生命周期和启动模式
-
linux系统中 redis 保存数据的5种形式 linux后端模式启动 jedis无法通过IP地址和端口号访问如何修改linux防火墙
-
Android面试题-----Activity的启动模式和应用场景
-
Android从零单排关于activity的几个问题 -启动模式和Flags
-
Activity的四种启动模式和用法场景
-
android开发实例(activity生命周期和启动模式、IPC机制)
-
Activity启动模式的选择