Activity启动模式&Task栈

简介: 在AMS中,ActivityRecord对应一个Activity,TaskRecord对应一个Task,每个TaskRecord中保存了若干ActivityRecord,TaskRecord由taskId标识,通过getTaskId()可以获取Activity所属的Task。

在AMS中,ActivityRecord对应一个Activity,TaskRecord对应一个Task,每个TaskRecord中保存了若干ActivityRecord,TaskRecord由taskId标识,通过getTaskId()可以获取Activity所属的Task。ActivityStack中的TaskRecord列表保存了AMS中所有的Task信息,ActivityStack、Task、Activity三者的对应关系如下:

img_a30eb7e53471476d48a9dccbf4560d45.jpe
Activity Task ActivityStack对应关系

其中ActivityStack处于ASM所属进程,ActivityThread在APP所属进程。可以看到ActivityStack中保存了TaskRecord列表,TaskRecord中保存了Activityrecord列表。在创建或者回退Activity的时候,就是对ActivityStack中TaskRecord的ActivityRecord进行出栈和入栈操作。

Task图示法:

img_7402b2546d47870d6bd862ff350a34b8.png

上图是本文Task的图示法,Task标记为“Task_task名(TaskAffinity)”,Task的TaskAffinity属性是其根Activity的TaskAffinity值;Task中的Activity标记为"应用名_Activity名(TaskAffinity)”,Activity的TaskAffinity通过AndroidManifest.xml中配置。中间的黑线表示Home,黑线下方是点home键退到后台的Task。

taskAffinity:

AndroidManifest.xml中可以给应用配置taskAffinity属性,不设置默认会使用应用的包名作为taskAffinity,taskAffinity只有在FLAG_ACTIVITY_NEW_TASK、singleTask、singleInstance的时候有效。桌面打开应用,由于Launcher使用了FLAG_ACTIVITY_NEW_TASK来启动应用,所以会根据主界面设置的taskAffinity或者默认的taskAffinity来查找或创建新的Task,具有相同taskAffinity的Task推到前台。

也就是说,应用没有启动是点击桌面应用图标会启动一个新的Task,如果应用已经启动,退到home后台后,再次点击图标,会将home后台的task原封不动重新推到前台,如果rootActivity是singleTask的话,就会将rootActivity上面的Activity对象出栈,然后再将Task推到前台。

Activity启动模式

1、standard

img_c9782daeb3762434bf5b40c32dc3bd8a.png
入栈

A_b、B_c都是standard模式,A_a启动了A_b,A_b启动了B_c,B_c又启动了A_b,可以得出如下结论:被启动的Activity如果是standard模式(即使启动者和被启动者位于不同的APP),那么被启动的Activity会保存在启动者所属的Task中(不论是否设置了TaskAffinity属性),且不论该Task中是否存在被启动实例,都会创建一个新的实例压入栈中。在按back键时也是一个一个的出栈。

2、singleTop

img_48eab37f892f2dee51f97022f2cd3c14.png

应用A启动应用B的B_b Activity,B_b是singleTop启动模式,那么再次启动B_b时,如果栈顶元素是B_b,就不再创建B_b实例。不论启动者和被启动者是否属于同一个应用,被启动的Activity和启动的Activity都会在同一个Task中。

singleTop属性和allowTaskReparenting混合使用会出现什么场景?

allowTaskReparenting

假设所有的Activity启动模式都是standard的,这里没有加上TaskAffinity,表明该属性不影响Task的归属。有如下运行流程:

img_506063c18c6c2c7c60d83865347bee7f.png

这里A_b设置了allowTaskReparenting=true;

1)、首先启动了应用A的主界面A_a,然后A_a启动了A_b,A_a和A_b都在Task_a中,这时按下home键

2)、启动应用B的主界面B_c,B_c启动了B_d,B_d又启动了A_b,由于A_b是standard的,所以B_c、B_d和A_b在同一个Task_b中,这时再按下home键

3)、再次点击桌面的A应用,系统会把Task列表中Task_a推到前台,由于A_b设置了allowTaskReparenting属性,所以Task_b中的A_b实例也被转移到了Task_a中。这就是allowTaskReparenting属性的作用,该属性在task从后台切换到前台的时候起作用。

如果设置A_b的启动模式为singleTop,上面的执行流程依然正确,但是却发现Task_a的栈顶有两个A_b实例。

3、singleTask

当一个singleTask 模式的activity 被启动时,AMS会检查是否存在与该activity 的taskAffinity 相同的task。

1)如果存在这样的task,那么检查该activity 是否已经创建,如果已创建,那么销毁在该Activity 以上的activity 并调用onNewIntent。如果activity 尚未实例化,那么创建该activity 的实例,并压入该task 栈。最后然后将该task 推到最上面。

2)如果不存在这样的task,那么就重新创建task,然后创建该activity 的实例,并入栈,然后将该task 推到最上面。

img_a513197a40f7ef2e00453b583b42f076.png
没有Home键参与

在没有Home键参与的场景下,A_b和B_b是SingleTask的,首先启动A应用的主界面A_a,然后启动A_a启动B应用的B_b,由于B_b是singleTask的,所以创建了新的Task_b。B_b启动应用A的A_b,由于A_b是singleTask的,所以没有在Task_b中创建A_b实例,而是先查找到A_b对应的Task_a,然后在Task_a中创建A_b实例,并将Task_a推到最顶层。

img_78f02f984c59e8f5bdeb760121898414.png
home场景1

启动A应用的主界面A_a,A_a是singleTask的,然后启动了A_b、A_c,这时按下home键,接着从前应用A,发现A_a上面的Activity都被弹出栈了。

img_fbcc8af484a474bea511d29a9fc2d6ac.png
home场景2

场景2中A_b是singleTask的,由于A_b不是rootActivity,所以没有出现Activity出栈操作。

img_0a073626fb2d3812db6498f8e3f0f70d.png
home场景3

首先启动应用A的主界面A_a,然后启动A_b,A_b是singleTask的,且taskAffinity和A_a一致,按下home键之后再启动B应用的主界面B_b,生成可新的Task_b,这时候B_b启动A_b,由于A_b是singleTask的,所以B_b从Task_a中移除了,Task_a位于顶部了。

img_7325064b9220299e9315e49a817e7428.png
home场景4

启动A应用的主界面A_a,位于Task_a中,然后按下home键,启动应用B的主界面B_a,B_a位于Task_b中,这时候B_a启动B_b,由于B_b是singleTask的,且taskAffinity和A_a的taskAffinity一致,所以Task_a会推到前台,然后将B_b的实例塞入Task_a中。

4、singleInstance

该启动模式的activity被启动时,如果activity没有被实例化,就创建一个新的task来保存activity实例;如果已经被实例化过,就调用其onNewIntent函数,该activity所在的Task中不允许存在其余activity。

img_aa1e61be25d7b8742c6b06ec722c0961.png

先确定应用A的主界面A_a,然后启动singleInstance的A_b,这里就创建了新的Task_b来保存A_b,然后A_b启动A_c,由于A_b具有独占Task的特性,所以需要先查找A_c的taskAffinity(不设置默认使用包名作为Taskname)对应的task是否存在,如果不存在就创建新的Task,如果存在就将Task推到前台,这里将Task_a推到了前台,并将A_c压入。

5、FLAG_ACTIVITY_NEW_TASK

启动Activity如果使用这个属性,AMS会尝试查找被启动的Activity的taskAffinity相同的Task,如果有,就直接将Task移动到前台,然后将启动的Activity压入栈;如果没有,则创建一个新的栈来保存实例。

img_bca41590fb5fa1d0d9840574d38c816a.png
目录
相关文章
|
20天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
103 9
|
11天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
19 1
|
14天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
17天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
19天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
46 4
|
23天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
数据结构(栈与列队)
数据结构(栈与列队)
20 1
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
31 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
初步认识栈和队列
初步认识栈和队列
60 10
|
1月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
48 3