明修"栈"道——越过Android启动栈陷阱( 五 )

3.3.2 关键流程分析
(1)初始化
startActivityInner()是最主要的方法 , 如下列几张图所示 , 该方法会率先调用setInitialState() , 初始化各类全局变量 , 并调用reset() , 重置ActivityStarter中各种状态 。
在此过程中 , 我们记下两个关键变量mMovedToFront和mAddingToTask , 它们均在此被重置为false 。
其中 , mMovedToFront代表当Task可复用时 , 是否需要将目标Task移动到前台;mAddingToTask代表是否要将Activity加入到Task中 。
 

明修"栈"道——越过Android启动栈陷阱

文章插图
 
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
(2)计算确认启动时的flag
该步骤会通过computeLaunchingTaskFlags()方法 , 根据launchMode、来源Activity的属性等进行初步计算 , 确认LaunchFlags 。
此处重点处理来源Activity为空的各类场景 , 与我们上文中的几种场景无关 , 故不再展开讲解 。
(3)获取可以复用的Task
该步骤通过调用getReusableTask()实现 , 用来查找有没有可以复用的Task 。
先说结论:场景0123中 , 都能获取到可以复用的Task , 而场景4中 , 未获取到可复用的Task 。
为什么场景4不可以复用?我们看一下getReusableTask()的关键实现 。
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
上图(标注1)中 , putIntoExistingTask代表是否能放入已经存在的Task 。当flag含有NEW_TASK且不含MULTIPLE_TASK时 , 或指定了singleInstance或singleTask的launchMode等条件 , 且没有指定Task或要求返回结果 时 , 场景01234均满足了条件 。
然后 , 上图(标注2)通过findTask()查找可以复用的Task , 并将过程中找到的栈顶Activity赋值给intentActivity 。最终 , 上图(标注3)将intentActivity对应的Task作为结果 。
findTask()是怎样查找哪个Task可以复用呢?
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
主要是确认两种结果mIdealRecord——“理想的ActivityRecord”  和 mCandidateRecord——"候选的ActivityRecord" , 作为intentActivity , 并取intentActivity对应的Task作为复用Task 。
什么ActivityRecord才是理想或候选的ActivityRecord呢?
在mTmpFindTaskResult.process()中确认 。
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
程序会将当前系统中所有的Task进行遍历 , 在每个Task中 , 进行如上图所示的工作——将Task的底部Activity realActivity与目标Activity cls进行对比 。
场景012中 , 我们想跳转Activity2 , 即cls是Activity2 , 与Task底部的realActivity2相同 , 则将该Task顶部的Activity3 r作为“理想的Activity”;
场景3中 , 我们想跳转Activity3 , 即cls是Activity3 , 与Task底部的realActivity2不同 , 则进一步判断task底部Activity2与目标Activity3的栈亲和行 , 具有相同亲和性 , 则将Task的顶部Activity3作为“候选Activity”;
场景4中 , 所有条件都不满足 , 最终没能找到可复用的Task 。在执行完getReusableTask()后将mAddingToTask赋值为true
由此 , 我们就能解释【场景4】中 , 新建了Task的现象 。
(4)确定是否需要将目标Task移动到前台
如果存在可复用的Task , 场景0123会执行recycleTask() , 该方法中会相继进行几个操作:setTargetRootTaskIfNeeded()、
complyActivityFlags() 。
首先 , 程序会执行
setTargetRootTaskIfNeeded() , 用来确定是否需要将目标Task移动到前台 , 使用mMovedToFront作为标识 。
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
在【场景123】中 , 来源Task和目标Task是不同的 , differentTopTask为true , 再经过一系列Task属性对比 , 能够得出mMovedToFront为true;


推荐阅读