在AMS中,ActivityRecord对应一个Activity,TaskRecord对应一个Task,每个TaskRecord中保存了若干ActivityRecord,TaskRecord由taskId标识,通过getTaskId()可以获取Activity所属的Task。ActivityStack中的TaskRecord列表保存了AMS中所有的Task信息,ActivityStack、Task、Activity三者的对应关系如下:
其中ActivityStack处于ASM所属进程,ActivityThread在APP所属进程。可以看到ActivityStack中保存了TaskRecord列表,TaskRecord中保存了Activityrecord列表。在创建或者回退Activity的时候,就是对ActivityStack中TaskRecord的ActivityRecord进行出栈和入栈操作。
Task图示法:
上图是本文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
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
应用A启动应用B的B_b Activity,B_b是singleTop启动模式,那么再次启动B_b时,如果栈顶元素是B_b,就不再创建B_b实例。不论启动者和被启动者是否属于同一个应用,被启动的Activity和启动的Activity都会在同一个Task中。
singleTop属性和allowTaskReparenting混合使用会出现什么场景?
allowTaskReparenting:
假设所有的Activity启动模式都是standard的,这里没有加上TaskAffinity,表明该属性不影响Task的归属。有如下运行流程:
这里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 推到最上面。
在没有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推到最顶层。
启动A应用的主界面A_a,A_a是singleTask的,然后启动了A_b、A_c,这时按下home键,接着从前应用A,发现A_a上面的Activity都被弹出栈了。
场景2中A_b是singleTask的,由于A_b不是rootActivity,所以没有出现Activity出栈操作。
首先启动应用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位于顶部了。
启动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。
先确定应用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压入栈;如果没有,则创建一个新的栈来保存实例。