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


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

文章插图
一、问题及背景应用间相互联动、相互跳转 , 是实现系统整体性、体验一致性的重要手段 , 也是最简单的一种方法 。
当我们用最常用的方法去startActivity时 , 竟也会遇到失败的情况 。在真实业务中 , 就遇到了这样一例异常:用户点击某个按钮时 , 想要“简简单单”跳转另一个应用 , 却没有任何反应 。
经验丰富的你 , 脑海中是否涌现出了各种猜想:是不是目标Activity甚至目标App不存在?是不是目标Activty没有对外开放?是不是有权限的限制或者跳转的action/uri错了……
真实的原因被flag、launchMode、Intent等特性层层藏匿 , 可能超出你此时的思考 。
本文将从源码出发 , 探究前因后果 , 展开讲讲在startActivity()真正准备启动一个Activity前 , 需要经过哪些“磨难” , 怎样有据可依地解决由栈问题导致的启动异常 。
1.1 业务中遇到的问题业务中的场景是这样的 , 存在A、B、C三个应用 。
(1)从应用A-Activity1跳转至应用B-Activity2;
(2)应用B-Activity2继续跳转到应用C-Activity3;
(3)C内某个按钮 , 会再次跳转B-Activity2 , 但点击后没有任何反应 。如果不经过前面A到B的跳转 , C直接跳到B是可以的 。
 
明修"栈"道——越过Android启动栈陷阱

文章插图
 
1.2 问题代码3个Activity的Androidmanifest配置如下 , 均可通过各自的action拉起 , launchMode均为标准模式 。
<!--应用A--><activityandroid:name=".Activity1"android:exported="true"><intent-filter><action android:name="com.zkp.task.ACTION_TO_A_PAGE1" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><!--应用B--><activityandroid:name=".Activity2"android:exported="true"><intent-filter><action android:name="com.zkp.task.ACTION_TO_B_PAGE2" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><!--应用C--><activityandroid:name=".Activity3"android:exported="true"><intent-filter><action android:name="com.zkp.task.ACTION_TO_C_PAGE3" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity>A-1到B-2的代码 , 指定flag为
FLAG_ACTIVITY_NEW_TASK
private void jumpTo_B_Activity2_ByAction_NewTask() {Intent intent = new Intent();intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);}B-2到C-3的代码 , 未指定flag
private void jumpTo_C_Activity3_ByAction_NoTask() {Intent intent = new Intent();intent.setAction("com.zkp.task.ACTION_TO_C_PAGE3");startActivity(intent);}C-3到B-2的代码 , 与A-1到B-2的完全一致 , 指定flag为 FLAG_ACTIVITY_NEW_TASK
private void jumpTo_B_Activity2_ByAction_NewTask() {Intent intent = new Intent();intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);}1.3 代码初步分析仔细查看问题代码 , 在实现上非常简单 , 有两个特征:
(1)如果直接通过C-3跳B-2 , 没有任何问题 , 但A-1已经跳过B-2后 , C-3就失败了 。
(2)在A-1和C-3跳到B-2时 , 都设置了flag为FLAG_ACTIVITY_NEW_TASK 。
依据经验 , 我们推测与栈有关 , 尝试将跳转前栈的状态打印出来 , 如下图 。
 
明修&quot;栈&quot;道——越过Android启动栈陷阱

文章插图
 
由于A-1跳到B-2时设置了FLAG_ACTIVITY_NEW_TASK , B-2跳到C-3时未设置 , 所以1在独立栈中 , 2、3在另一个栈中 。示意如下图 。
 
明修&quot;栈&quot;道——越过Android启动栈陷阱

文章插图
 
C-3跳转B-2一般有3种可能的预期 , 如下图:预想1 , 新建一个Task , 在新Task中启动一个B-2;预想2 , 复用已经存在的B-2;预想3 , 在已有Task中新建一个实例B-2 。
 
明修&quot;栈&quot;道——越过Android启动栈陷阱


推荐阅读