打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
探索Activity之启动Intent Flag和taskAffinity
引用上文生命周期launchMode介绍, Activity的生命周期实际上比我们想象的复杂得多.
本文主要通过实例, 来探索下Activity的启动Intent Flag以及taskAffinity对生命周期和Task/Back Stack的影响. 算是对生命周期launchMode的一个补充, 以便我们在开发过程中灵活组合运用.
照例, 我们先从一些官方解释开始:
1, 相关概念
对生命周期和Task/Back Stack有影响的Intent Flag主要有:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
FLAG_ACTIVITY_NEW_TASK
会产生与 "singleTask" launchMode 值相同的行为.
在新任务中启动Activity. 如果已有包含该Activity的任务,则该任务会转到前台并恢复其最后状态,同时该Activity会在onNewIntent()中收到新Intent.
FLAG_ACTIVITY_SINGLE_TOP
会产生与 "singleTop" launchMode 值相同的行为.
如果正在启动的Activity是当前Activity(位于返回栈的顶部), 则现有实例会接收对 onNewIntent()的调用,而不是创建 Activity 的新实例.
FLAG_ACTIVITY_CLEAR_TOP
如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例.
如果指定 Activity 的启动模式为 "standard",则该 Activity 也会从堆栈中删除,并在其位置启动一个新实例,以便处理传入的 Intent。 这是因为当启动模式为 "standard" 时,将始终为新 Intent 创建新实例.
以上为官方文档解释.
探索Activity之launchMode一文中我们也提到了实际上文档由于"年久失修"没有跟上, 有些解释是不合理的.
我们可以跟随实例一起看下.
2, 开始探索
借用上次探索生命周期的Demo程序.
Github源码地址
通过AActivity, BActivity, CActivity这三个Activity之间的跳转来进行intent flag的探索.
如果没有特别之处, 默认A, B, C三个Activity的launchMode都是默认的standard模式.
2.1, FLAG_ACTIVITY_NEW_TASK
2.1.1, 执行B -> A, B启动A时加FLAG_ACTIVITY_NEW_TASK
实验目的是看下, 在当前系统没有A实例时, 用FLAG_ACTIVITY_NEW_TASK来启动A会不会将A创建在单独的任务中.
BActivity.java中:
startActivity(new Intent(BActivity.this, AActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=3: Task id #35 * TaskRecord{42b60ae0 #35 A=com.anly.samples U=0 sz=3} numActivities=3 rootWasReset=false userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{4285c1b0 u0 com.anly.samples/.MainActivity t35}, ActivityRecord{42decc00 u0 com.anly.samples/.activity.BActivity t35}, ActivityRecord{4372b9e8 u0 com.anly.samples/.activity.AActivity t35}]
可以看到:
B以FLAG_ACTIVITY_NEW_TASK启动A, A仍然和B处在同一个Task中.
2.1.2 执行A -> B -> A, B启动A时加FLAG_ACTIVITY_NEW_TASK
实验目的是想验证下官方文档对FLAG_ACTIVITY_NEW_TASK的解释, 在A实例已经存在的情况下, 以FLAG_ACTIVITY_NEW_TASK启动A会发生什么.
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=2: Task id #34 * TaskRecord{42bfb088 #34 A=com.anly.samples U=0 sz=4} numActivities=4 rootWasReset=false userId=0 mTaskType=0 numFullscreen=4 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{42568318 u0 com.anly.samples/.MainActivity t34}, ActivityRecord{42725050 u0 com.anly.samples/.activity.AActivity t34}, ActivityRecord{42dab240 u0 com.anly.samples/.activity.BActivity t34}, ActivityRecord{42e293f8 u0 com.anly.samples/.activity.AActivity t34}]
可以看到:
1, 在B启动A之前, Task #34中本来就有A, 但是B加FLAG_ACTIVITY_NEW_TASK启动A时, A并未重用, 而是在本Task #34中在此创建了一个A的实例. (这点和文档描述不一致)
2, 此时Task #34中的Activity实例为ABA.
3, 但是如果A的lunchMode是singleTask的话, 如lunchMode一文2.2.3所示, 此时应该销毁A以上的实例, Task中只剩下A.
4, 综上, FLAG_ACTIVITY_NEW_TASK并不等同与singleTask. 且FLAG_ACTIVITY_NEW_TASK感觉并为起作用(在A已经存在一个实例的情况下).
2.2, FLAG_ACTIVITY_SINGLE_TOP
2.2.1, 执行A -> B -> B, 其中B启动B时加FLAG_ACTIVITY_SINGLE_TOP
BActivity启动B时加:
startActivity(new Intent(BActivity.this, BActivity.class) .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=6: Task id #38 * TaskRecord{43665a30 #38 A=com.anly.samples U=0 sz=3} numActivities=3 rootWasReset=true userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{42bbfea0 u0 com.anly.samples/.MainActivity t38}, ActivityRecord{433b6130 u0 com.anly.samples/.activity.AActivity t38}, ActivityRecord{4324ef18 u0 com.anly.samples/.activity.BActivity t38}]
可以看到:
1, B复用了, 通过onNewIntent, 走onResume流程, 复用之前的B实例.
2, 此时Task #38中的Activity实例为AB.
2.3, FLAG_ACTIVITY_CLEAR_TOP
2.3.1, 执行A -> B -> A, 启动B启动A时加FLAG_ACTIVITY_CLEAR_TOP
实验目的是为了看下A会不会重用, 且B会不会被Clear.
BActivity启动A的代码:
startActivity(new Intent(BActivity.this, AActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=7: Task id #39 * TaskRecord{4274e020 #39 A=com.anly.samples U=0 sz=2} numActivities=2 rootWasReset=false userId=0 mTaskType=0 numFullscreen=2 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{42742598 u0 com.anly.samples/.MainActivity t39}, ActivityRecord{4274eb28 u0 com.anly.samples/.activity.AActivity t39}]
可以看到:
1, A并没有重用, 而是新建了一个实例. 这个和文档是一致的, 参见FLAG_ACTIVITY_CLEAR_TOP概念第二点.
2, B被销毁了(Clear Top).
3, 此时Task #39中只有A(一个新的A).
2.4, 组合使用
以上是简单使用, 然后实际场景中会有很多组合使用Intent Flag以及Intent Flag与taskAffinity结合使用的情况. 其中官方文档就提到了:
FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK 结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。
下面我们来实验下这种组合:
2.4.1, FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_NEW_TASK
执行A -> B -> A, 其中B启动A时, Intent flag加FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK
B启动A的代码:
startActivity(new Intent(BActivity.this, AActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK));
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=8: Task id #40 * TaskRecord{429c96b0 #40 A=com.anly.samples U=0 sz=2} numActivities=2 rootWasReset=false userId=0 mTaskType=0 numFullscreen=2 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{427907d0 u0 com.anly.samples/.MainActivity t40}, ActivityRecord{42dd24b8 u0 com.anly.samples/.activity.AActivity t40}]
可以看到:
1, 结果和2.3.1单独使用Intent.FLAG_ACTIVITY_CLEAR_TOP是一样的.
2.5, taskAffinity补充实验
有2.1.1, 2.1.2以及2.4.1这三个包含Intent.FLAG_ACTIVITY_NEW_TASK的实验, 可以看到, 字面上Intent.FLAG_ACTIVITY_NEW_TASK的意思是在新的task启动Activity, 然而事实上, 新Activity还是在原来的task上创建的.
这里有必要提出官网关于taskAffinity的解释了:
taskAffinity指示Activity优先属于哪个task. 默认情况下, 同一应用中的所有 Activity 彼此关联. 因此, 默认情况下, 同一应用中的所有 Activity 优先位于相同任务中.
taskAffinity在两种情况下会起作用:
--- 启动 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 标志.
--- Activity 将其 allowTaskReparenting 属性设置为 "true".
让我们来结合taskAffinity做下实验:
2.5.1, taskAffinity + FLAG_ACTIVITY_NEW_TASK
设置A和B不同的taskAffinity, 执行Main -> A -> B -> A -> B -> A, 其中B启动A使用FLAG_ACTIVITY_NEW_TASK
为什么要执行两次B -> A? 我们跟随实验结果, 稍后来看.
设置A的taskAffinity为com.anly.aactivity, B默认(包名).
<activity android:name=".activity.AActivity" android:label="A-Activity" android:taskAffinity="com.anly.aactivity" ></activity><activity android:name=".activity.BActivity" android:label="B-Activity" ></activity>
B启动A:
startActivity(new Intent(BActivity.this, AActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=10: Task id #44 * TaskRecord{43085768 #44 A=com.anly.aactivity U=0 sz=2} numActivities=2 rootWasReset=false userId=0 mTaskType=0 numFullscreen=2 mOnTopOfHome=false affinity=com.anly.aactivity intent={flg=0x10000000 cmp=com.anly.samples/.activity.AActivity} realActivity=com.anly.samples/.activity.AActivity Activities=[ActivityRecord{4303fe00 u0 com.anly.samples/.activity.AActivity t44}, ActivityRecord{4324bb10 u0 com.anly.samples/.activity.BActivity t44}] Task id #43 * TaskRecord{426d0a78 #43 A=com.anly.samples U=0 sz=3} numActivities=3 rootWasReset=true userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{4256ae80 u0 com.anly.samples/.MainActivity t43}, ActivityRecord{4372db08 u0 com.anly.samples/.activity.AActivity t43}, ActivityRecord{4273f478 u0 com.anly.samples/.activity.BActivity t43}]
可以看到:
1, Stack #1中有两个Task, #43(affinity=com.anly.samples)和#44(affinity=com.anly.aactivity).
2, 第一轮Main -> A -> B时, 再Task #43中产生了Main,A,B三个Activity实例.
3, 接着B -> A时, A在一个新的Task #44中创建了新的A实例.
4, 然后A -> B, 因为B不加任何参数(启动模式, affinity, flag等), B会创建在启动他的Activity也就是A所在的Task.
5, 此时Task #44中就有了A, B.
6, 再次在B中点击启动A(携带Intent.FLAG_ACTIVITY_NEW_TASK)时. 并没有任何反应.
为什么会出现第6点描述的这样的问题呢?
我理解:
此时B启动A, 因为携带Intent.FLAG_ACTIVITY_NEW_TASK, 且A的taskAffnity为"com.anly.aactivity". 系统会在affinity=com.anly.aactivity的Task中找有没有已经存在的A的实例, 发现Task #44中有. 于是乎, 想重用A. 然而并没有能销毁B, 让A弹出来接收新的Intent.
所以说, 这种情况下, Intent.FLAG_ACTIVITY_NEW_TASK必须结合Intent.FLAG_ACTIVITY_CLEAR_TOP来一起用.
让我们再做个实验验证下想法.
2.5.1, taskAffinity + FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP
设置A和B不同的taskAffinity, 执行Main -> A -> B -> A -> B -> A, 其中B启动A使用FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP
A的affinity还是设置成com.anly.aactivity, B默认.
B启动A的代码:
startActivity(new Intent(BActivity.this, AActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));
生命周期Log:
Task/Back Stack信息:
Stack #1 mStackId=11: Task id #46 * TaskRecord{4338bc08 #46 A=com.anly.aactivity U=0 sz=1} numActivities=1 rootWasReset=false userId=0 mTaskType=0 numFullscreen=1 mOnTopOfHome=false affinity=com.anly.aactivity intent={flg=0x14000000 cmp=com.anly.samples/.activity.AActivity} realActivity=com.anly.samples/.activity.AActivity Activities=[ActivityRecord{42d88890 u0 com.anly.samples/.activity.AActivity t46}] Task id #45 * TaskRecord{42eee4d0 #45 A=com.anly.samples U=0 sz=3} numActivities=3 rootWasReset=false userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true affinity=com.anly.samples intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity} realActivity=com.anly.samples/.MainActivity Activities=[ActivityRecord{42ed9690 u0 com.anly.samples/.MainActivity t45}, ActivityRecord{42e507b8 u0 com.anly.samples/.activity.AActivity t45}, ActivityRecord{42714cd0 u0 com.anly.samples/.activity.BActivity t45}]
可以看到:
果然, 此时第二次B -> A, 有效果了, 跳转到A了.
然而, 我们发现, 虽然Task #46中只有一个A(B被clear top,销毁了). 然而A并不是重用的, 而是先销毁了然来的A实例, 重建了一个A实例.
参见相关概念Intent.FLAG_ACTIVITY_CLEAR_TOP的第二点解释, 的确是这样, 因为A是默认的standard模式, 所以必须新创建实例.
3, 结论
至此, 我们关于Intent flag和taskAffinity的实验结束, 我们来看下相关结论:
FLAG_ACTIVITY_NEW_TASK并不像官方文档所说的等同与singleTask.
在没有任何其他flag组合和taskAffinity设置的情况下, 同一应用内FLAG_ACTIVITY_NEW_TASK启动另外一个Activity, 不会在新的Task中创建实例, 也不会有实例复用.
FLAG_ACTIVITY_SINGLE_TOP作用等同与singleTop, 当Task的top Activity是该Activity时, Activity复用.
FLAG_ACTIVITY_CLEAR_TOP会clear top, 也就是说如果Task中有ABCD, 在D中启动B, 会clear掉B以上的CD. CD销毁.
注意, FLAG_ACTIVITY_CLEAR_TOP并不意味着重用, 默认Activity为standard模式的话, 只是会clear其top的其他Activity实例, 该Activity并不会重用, 而是也会销毁, 然后创建一个新的该Activity实例来响应此Intent.
在没有设置taskAffinity的情况下, 同一应用内FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP组合启动另外一个Activity, 作用和单独使用FLAG_ACTIVITY_CLEAR_TOP是一样.(此点类同与第二点)
如taskAffinity解释的一样, 在我们没有引入taskAffinity的2.1, 2.2, 2.3, 2.4的相关实验中, 同一个应用中, 使用各种Intent flag都并不会创建新的Task.
taskAffinity需结合FLAG_ACTIVITY_NEW_TASK使用, 此时会再新的Task中寻找/创建待启动的Activity实例.
强烈建议FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP结合使用, 单独使用FLAG_ACTIVITY_NEW_TASK可能会遇到2.5.1那样的问题.
Intent Flag并不能代替launchMode, 至少在想重用Activity的情况下, 你需要做的是考虑launchMode而非Intent Flag.
个人理解, Intent Flag更多是倾向于用来做Task中的Activity组织. 而launchMode兼顾Task组织和Activity实例的重用.
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
总结篇之三:Activity的task相关
android之intent的Flag详解
【Android】任务和返回栈(task and back stack)
AndroidManifest详解之Application(有图更好懂)
Intent(FLAG_ACTIVITY_CLEAR_TOP FLAG_ACTIVITY_NEW_TASK)
Activity启动模式及IntentFlags与栈的关联分析
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服