引用上文生命周期和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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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默认(包名).

<activityandroid:name=".activity.AActivity"android:label="A-Activity"android:taskAffinity="com.anly.aactivity">
</activity><activityandroid: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=falseaffinity=com.anly.aactivityintent={flg=0x10000000 cmp=com.anly.samples/.activity.AActivity}realActivity=com.anly.samples/.activity.AActivityActivities=[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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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=falseaffinity=com.anly.aactivityintent={flg=0x14000000 cmp=com.anly.samples/.activity.AActivity}realActivity=com.anly.samples/.activity.AActivityActivities=[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=trueaffinity=com.anly.samplesintent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}realActivity=com.anly.samples/.MainActivityActivities=[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的实验结束, 我们来看下相关结论:

  1. FLAG_ACTIVITY_NEW_TASK并不像官方文档所说的等同与singleTask.

  2. 在没有任何其他flag组合和taskAffinity设置的情况下, 同一应用内FLAG_ACTIVITY_NEW_TASK启动另外一个Activity, 不会在新的Task中创建实例, 也不会有实例复用.

  3. FLAG_ACTIVITY_SINGLE_TOP作用等同与singleTop, 当Task的top Activity是该Activity时, Activity复用.

  4. FLAG_ACTIVITY_CLEAR_TOP会clear top, 也就是说如果Task中有ABCD, 在D中启动B, 会clear掉B以上的CD. CD销毁.

  5. 注意, FLAG_ACTIVITY_CLEAR_TOP并不意味着重用, 默认Activity为standard模式的话, 只是会clear其top的其他Activity实例, 该Activity并不会重用, 而是也会销毁, 然后创建一个新的该Activity实例来响应此Intent.

  6. 在没有设置taskAffinity的情况下, 同一应用内FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP组合启动另外一个Activity, 作用和单独使用FLAG_ACTIVITY_CLEAR_TOP是一样.(此点类同与第二点)

  7. 如taskAffinity解释的一样, 在我们没有引入taskAffinity的2.1, 2.2, 2.3, 2.4的相关实验中, 同一个应用中, 使用各种Intent flag都并不会创建新的Task.

  8. taskAffinity需结合FLAG_ACTIVITY_NEW_TASK使用, 此时会再新的Task中寻找/创建待启动的Activity实例.

  9. 强烈建议FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP结合使用, 单独使用FLAG_ACTIVITY_NEW_TASK可能会遇到2.5.1那样的问题.

  10. Intent Flag并不能代替launchMode, 至少在想重用Activity的情况下, 你需要做的是考虑launchMode而非Intent Flag.

  11. 个人理解, Intent Flag更多是倾向于用来做Task中的Activity组织. 而launchMode兼顾Task组织和Activity实例的重用.

探索Activity之启动Intent Flag和taskAffinity相关推荐

  1. Activity栈管理(三):Intent的Flag与taskAffinity

    作者:anly_jun 链接:https://www.jianshu.com/p/c97688eb5056 引用上文生命周期和launchMode介绍, Activity的生命周期实际上比我们想象的复 ...

  2. Activity启动模式和FLAG、TASKAFFINITY

    Activity的任务栈Task以及启动模式与Intent的Flag详解 转载至 http://www.jianshu.com/p/c1386015856a 什么是任务栈(Task) 官方文档是这么解 ...

  3. Activity的启动模式与flag详解

    Activity有四种加载模式:standard(默认), singleTop, singleTask和 singleInstance.以下逐一举例说明他们的区别: standard:Activity ...

  4. 文化袁探索专栏——Activity|Application启动流程

    文化袁探索专栏--Activity.Window和View三者间关系 文化袁探索专栏--View三大流程#Measure 文化袁探索专栏--View三大流程#Layout 文化袁探索专栏--消息分发机 ...

  5. Android 源码分析 Activity的启动模式

    一直想抽空针对AMS进行源码分析,无奈一方面因为很忙,另外AMS很复杂,涉及的知识点也比较多,今天利用五一假期对AMS的一个方面,Activity的启动模式进行源码分析,这里面包括了ActivityR ...

  6. Activity的启动与管理之图记表查

    Activity 作为AndroidApp重要的组成之一,它的创建.运行和维护也是我们必须知悉的一部分:Activity的创建是由开发人员决定,它的运行与维护则由系统通过任务栈进行协助管理,我们通过配 ...

  7. 二、Activity 的启动模式

    一.四种启动模式: ① standard(标准模式) 这个是系统的默认模式.在这种模式下,每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在,并且谁启动了这个 Acti ...

  8. Activity的启动模式

    题目:Activity的启动模式,区别 这道题想考察什么? 启动模式是什么? 启动模式如何设置? Activity的启动模式区别? 应用场景以及那些注意的点? 考察的知识点 启动任务,返回栈,启动模式 ...

  9. 安卓activity的启动流程

    文章目录 前言 一.概述 二. 启动流程 2.1 Activity.startActivity 2.2 startActivityForResult 2.3 execStartActivity 2.4 ...

最新文章

  1. 独家 | 用Python Featuretools库实现自动化特征工程(附链接)
  2. 纠结的名字 - 自动生成apk文件名
  3. Kafka不停机,如何无感知迁移ZooKeeper集群?
  4. OpenCV的minEllipse函数(求轮廓的外接椭圆)一定要判断输入点是否大于5个
  5. java字符串10_十个最常见的Java字符串问题
  6. Spring MVC Hello World 例子
  7. 深度测试与alpha混合(1)
  8. 操作系统学习---进程
  9. Autodesk HSMWorks Ultimate 2019 Crack 破解版
  10. 炫界 (392) -(持法延迟)_火爆手游必不可少低延迟蓝牙耳机,双11精品优质蓝牙耳机推荐...
  11. 数学计算机sci,有哪些是数学类SCI
  12. 百度地图坐标转换及跨域
  13. 酒店客房预订管理系统简单实现
  14. nc 监听数据_linux包之nc之nc命令
  15. 互联网广告行业术语及缩写
  16. c语言写抢QQ红包软件,Android实现QQ抢红包插件
  17. Latex表格换行垂直居中对齐
  18. Error:A JNI error has occurred,please check your installation and try again
  19. 2022软科世界大学学术排名
  20. 英文学习20180206

热门文章

  1. 谷歌研发智能隐形眼镜
  2. 魔兽一直显示世界服务器无法连接,魔兽世界怀旧服世界服务器无法连接解决办法...
  3. 拓润服装ERP管理系统
  4. spring实战学习(三)配置方式
  5. 运行SHA256加密脚本.
  6. NOIP提高组初赛[选择题知识点汇总]
  7. 推荐一款手机清理工具App悟空清理
  8. 网络营销人员21条基本能力要求
  9. rnnlm源码分析(四)
  10. html中写meta会乱码,网页html代码不可缺少的5个meta标签属性