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


文章插图
 
但实际上3种预期都没有实现 , 所有Activity的任何声明周期都没有变化 , 界面始终停留在C-3 。
看一下FLAG_ACTIVITY_NEW_TASK的官方注释和代码注释 , 如下图:
 

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

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

文章插图
 
重点关注这一段:
When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in.
使用此flag时 , 如果你正在启动的Activity已经在一个Task中运行 , 那么一个新Activity不会被启动;相反 , 当前Task将简单地显示在界面的前面 , 并显示其最后的状态 。
——显然 , 官方文档与代码注释的表述与我们的异常现象是一致的 , 目标Activity2已经在Task中存在 , 则不会被启动;Task直接显示在前面 , 并展示最后的状态 。由于目标Activty3就是来源Activity3 , 所以页面没有任何变化 。
看起来官方还是很靠谱的 , 但实际效果真的能一直与官方描述一致吗?我们通过几个场景来看一下 。
二、场景拓展与验证2.1 场景拓展在笔者依据官方描述进行调整、复现的过程中 , 发现了几个比较有意思的场景 。
PS:上面业务的案例中 , B-2和C-3在不同应用内 , 又在相同的Task内 , 但实际上是否是同一个应用 , 对结果的影响并不大 。为了避免不同应用和不同Task造成阅读混乱 , 同一个栈的跳转 , 我们都在本应用内进行 , 故业务中的场景等价于下面的【场景0】
【场景0】把业务中B-2到C-3的应用间跳转改为B-2到B-3的应用内跳转
// B-2跳转B-3public static void jumpTo_B_3_ByAction_Null(Context context) {Intent intent = new Intent();intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3");context.startActivity(intent);}如下图 , A-1设置NEW_TASK跳转B-2 , 再跳转B-3 , 最终设置NEW_TASK想跳转B-2 。虽然跳C-3改为了跳B-3 , 但与之前问题的表现一致 , 没有反应 , 停留在B-3 。
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
有的读者会指出这样的问题:如果同一个应用内使用NEW_TASK跳转 , 而不指定目标的taskAffinity属性 , 实际是无法在新Task中启动的 。请大家忽略该问题 , 可以认为笔者的操作是已经加了taskAffinity的 , 这对最终结果并没有影响 。
【场景1】如果目标Task和来源Task不是同一个 , 情况是否会如官方文档所说复用已有的Task并展示最近状态?我们改为B-3启动一个新Task的新Activity C-4 , 再通过C-4跳回B-2
// B-3跳转C-4public static void jumpTo_C_4_ByAction_New(Context context) {Intent intent = new Intent("com.zkp.task.ACTION_TO_C_PAGE4");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);}// C-4跳转B-2public static void jumpTo_B_2_ByAction_New(Context context) {Intent intent = new Intent();intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);}如下图 , A-1设置NEW_TASK跳转B-2 , 再跳转B-3 , 再设置NEW_TASK跳转C-4 , 最终设置NEW_TASK想跳转B-2 。
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
预想的结果是:不会跳到B-2 , 而是跳到它所在Task的顶层B-3 。
实际的结果是:与预期一致 , 确实是跳到了B-3 。
【场景2】把场景1稍做修改:C-4到B-2时 , 我们不通过action来跳 , 改为通过setClassName跳转 。
// C-4跳转B-2public static void jumpTo_B_2_ByPath_New(Context context) {Intent intent = new Intent();intent.setClassName("com.zkp.b", "com.zkp.b.Activity2"); // 直接设置classname,不通过actionintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);}如下图 , A-1设置NEW_TASK跳转B-2 , 再跳转B-3 , 再设置NEW_TASK跳转C-4 , 最终设置NEW_TASK想跳转B-2 。
 


推荐阅读