Android基础Android开发经验谈Android技术知识

也聊聊activity的launchMode启动模式

2018-03-21  本文已影响1137人  sunhapper

activity启动模式是android开发中的基础知识,面试中也经常被问到,不过刚入门的时候看到网上众多介绍activity启动模式的文章,看过一遍感觉都懂了,实际碰到具体的场景有时候还是有点懵逼。

现在对这块的知识用的多了,也算有了一些心得,尝试着用简单的方式总结备忘下。

这篇文章主要是对于activity启动模式的相关知识的一个规律总结,基础的知识点会介绍的比较简略,适合已经大致了解了activity的启动模式,但对于具体场景分析还不太熟练的同学,看完这篇文章你应该就能按照一个既定的套路去分析activity的启动模式了

基础知识点

Task

启动模式

standard

默认的启动模式

singleTop

栈顶复用模式

singleTask

singleInstance

独享task

taskAffinity

个人简单理解为根activity的taskAffinity可以决定task的“名字”,activity在新task启动时和re-parent时需要根据taskAffinity来确定该activity会出现在哪个task

FLAG

除了在AndroidManifest.xml中指定launchMode之外,在设置Intent的FLAG同样会影响到activity的启动,这里只介绍下FLAG_ACTIVITY_NEW_TASK,其他的FLAG文章中暂时不会涉及,以后有空再补

FLAG_ACTIVITY_NEW_TASK

网上很多文章说在Intent中设置了FLAG_ACTIVITY_NEW_TASK相当于为Activity指定了singleTask模式,但是实际上singleTask模式具有三个特点

设置FLAG_ACTIVITY_NEW_TASK只是让一个activity具有了在其他task启动的能力,并没有后面两个特点,所以上面的说法其实是不准确的

FLAG_ACTIVITY_NEW_TASK

本文涉及到的基础知识就简单介绍完了,下面开始进入正题

activity启动规律总结

为了表述方便,后面统一用CurrentActivity和TargetActivity表示一个activity启动另一个activity的场景

把singleInstance拉出来单独说一下

首先判断TargetActivity是否只能在当前task启动

先列个表格

TargetActivity是否可以在新task中启动 standard singleTop singleTask singleInstance
设置FLAG_ACTIVITY_NEW_TASK true true true true
不设置FLAG_ACTIVITY_NEW_TASK false false true true

一句话总结:singleTask/singleInstance的activity本身具有在新task中启动的能力,standard/singleTop的activity要想拥有在新task中启动的能力,需要在设置Intent.FLAG_ACTIVITY_NEW_TASK

第二步判断TargetActivity所在的task

总共三种情况:

对于不具有在新task中启动能力的activity

未指定Intent.FLAG_ACTIVITY_NEW_TASK
standard/singleTop的activity,对不起,你没有这个能力,只能老老实实在当前task中启动。

当然如果当前应用一个task都没有,那么这个activity启动时会创建一个task,这个activity的实例作为task的根activity。

对于具有在新task中启动能力的activity

singleTask/singleInstance以及在Intent中设置了FLAG_ACTIVITY_NEW_TASK的standard/singleTop具有在新task中启动能力。

至于是否在一个新task中启动,还要受其他条件的限制,这个条件可以先简单的认为是taskAffinity

总结一下就是找一个taskAffinity的task去启动,找不到就新建一个(这里会忽略了singleInstance独占的task)

默认的taskAffinity

不指定taskAffinity的话默认是应用的包名,也可以为在application中指定所有的activity的taskAffinity。

优先级是activity中指定的taskAffinity>application中指定的taskAffinity>默认的包名

这里需要特别注意下,不少文章中说singleTask的TargetActivity想要在新的task中启动需要设置taskAffinity,其实真正原因是不设置的话,所有activity都用的默认的,也就是同一个应用中taskAffinity不指定的话默认都是相同的,按照上面的分析singleTask的TargetActivity肯定会在当前task启动的。

如果设置MainActivity或者说当前task的根activity的taskAffinity为一个不同的值话,不指定taskAffinity的singleTask的TargetActivity同样会在新的task中启动。

最后第三步根据TargetActivity的启动模式判断会如何启动

套路总结

返回的情况

返回的情况暂时只考虑按返回键的场景,而不考虑在系统的最近任务的中切换后的情况(以后有空单独写篇文章)

只考虑按返回键的场景其实比较简单,只需要了解总是

异常的场景

按照上面的思路分析已经可以覆盖到工作中大部分场景了,不过我在实践中还是碰到了一些异常的情况,也记录下,说不定对碰到同样问题的同学会有帮助

标记了FLAG_ACTIVITY_NEW_TASK的standard/singleTop的activity成为根activity的情况

异常描述

场景描述

上面说的可能大家有点一头雾水,那么我构建一个还算比较常见的场景

具体的验证代码参见NotificationActivity.kt

建议

对于在Service,Notification等必须加FLAG_ACTIVITY_NEW_TASK才能启动activity的地方需要特别谨慎

  1. 确保要启动的TargetActivity不会成为一个task的根activity
  2. 如果1不能保证,那么应当确保这些地方不会多次启动同一个Activity
  3. 如果1,2都不能保证,那么或许要启动的TargetActivity应该是singleTask的,至少singleTask多次启动时onNewIntent会被调用
  4. 如果singleTask真的适应不了业务场景,那么应当考虑用一个中转的Activity,先启动中转Activity,再在中转Activity中启动真正的TargetActivity

startActivityForResult的异常

先扔两个结论:

5.0之前

如果CurrentActivity为singleInstance或者TargetActivity为singleTask/singleInstance,则会在intent中加入FLAG_ACTIVITY_NEW_TASK标志

所以有如下表格

5.0及以上版本的情况

对于android 5.0以上的系统,其实自己也总结了一个规律:如果使用startActivityForResult,Intent中没有主动设置FLAG_ACTIVITY_NEW_TASK,那么在启动TargetActivity时会忽略activity的启动模式,在当前task的栈顶新建一个TargetActivity实例,以保证startActivityForResult可以正常工作。

上面的图看上去很美好,所有的情况都可以覆盖到,但是实际上如果真的用startActivityForResult启动singleTask/singleInstance的activity,会出现了很多预期之外的结果

举个例子

上图从上到下依次全部使用startActivityForResult启动,可以看出明显task中的activity状态不是很符合自己的预期

事实上,5.0以上系统使用startActivityForResult启动activity可以制造出出很多task中activity不符合预期的场景,基于这些task再用startActivity或者设置FLAG_ACTIVITY_NEW_TASK去启动activity又会衍生出更多的不符合预期的场景,这样的复杂程度老实说自己现在还hold不住,总结不出什么规律,只能具体场景用代码具体实践具体分析

一些关于startActivityForResult的建议

最后

相关的验证代码放在BasicPractice

上一篇 下一篇

猜你喜欢

热点阅读