Activity 的启动模式,真香警告

文章详细分析了Android中不同Activity启动模式(standard、singleTop、singleTask、singleInstance)的行为,以及如何通过Intent的FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_SINGLE_TOP和FLAG_ACTIVITY_CLEAR_TOP影响Activity在任务栈中的行为。重点强调了taskAffinity在不同模式下的作用以及Intentflag在动态场景中的应用。
摘要由CSDN通过智能技术生成

override fun onNewIntent(intent: Intent?) {

super.onNewIntent(intent)

log(“onNewIntent”)

}

override fun onDestroy() {

super.onDestroy()

log(“onDestroy”)

}

abstract fun getTip(): String

private fun log(log: String) {

Log.e(getTip(), log + " " + "hashCode: " + hashCode() + " " + "taskId: " + taskId)

}

}

class StandardActivity : BaseLaunchModeActivity() {

override fun getTip(): String {

return “StandardActivity”

}

}

四个 Activity 相继互相启动,查看输出的日志,可以看出 SingleTaskActivity 和 SingleInstanceActivity 均处于独立的任务栈中,而 StandardActivity 和 SingleTopActivity 处于同个任务栈中。说明 taskAffinity 对于 standard 和 singleTop 这两种模式不起作用

E/StandardActivity: onCreate hashCode: 31933912 taskId: 37

E/SingleTopActivity: onCreate hashCode: 95410735 taskId: 37

E/SingleTaskActivity: onCreate hashCode: 255733510 taskId: 38

E/SingleInstanceActivity: onCreate hashCode: 20352185 taskId: 39

再依次启动 SingleTaskActivity 和 SingleTopActivity。可以看到 SingleTaskActivity 被复用了,且在 38 这个任务栈上启动了一个新的 SingleTopActivity 实例。之所以没有复用 SingleTopActivity,是因为之前的 SingleTopActivity 是在 37 任务栈中,并非当前任务栈

E/SingleTaskActivity: onNewIntent hashCode: 255733510 taskId: 38

E/SingleTopActivity: onCreate hashCode: 20652250 taskId: 38

再启动一次 SingleTopActivity,两次 StandardActivity。可以看到 SingleTopActivity 的确在当前任务栈中被复用了,并均创建了两个新的 StandardActivity 实例。说明 singleTop 想要被复用需要当前任务栈的栈顶就是目标 Activity,而 standard 模式每次均会创建新实例

E/SingleTopActivity: onNewIntent hashCode: 20652250 taskId: 38

E/StandardActivity: onCreate hashCode: 252563788 taskId: 38

E/StandardActivity: onCreate hashCode: 25716630 taskId: 38

再依次启动 SingleTaskActivity 和 SingleInstanceActivity。可以看到 SingleTaskActivity 和 SingleInstanceActivity 均被复用了,且 SingleTaskActivity 之上的三个 Activity 均从任务栈中被弹出销毁了,SingleTaskActivity 成为了 task 38 新的栈顶 Activity

E/StandardActivity: onDestroy hashCode: 252563788 taskId: 38

E/SingleTopActivity: onDestroy hashCode: 20652250 taskId: 38

E/SingleTaskActivity: onNewIntent hashCode: 255733510 taskId: 38

E/StandardActivity: onDestroy hashCode: 25716630 taskId: 38

E/SingleInstanceActivity: onNewIntent hashCode: 20352185 taskId: 39

再依次启动 StandardActivity 和 SingleTopActivity。可以看到创建了一个新的任务栈,且启动的是两个新的 Activity 实例。由于 SingleInstanceActivity 所在的任务栈只会由其自身所独占,所以 StandardActivity 启动时就需要创建一个新的任务栈用来容纳自身

E/StandardActivity: onCreate hashCode: 89641200 taskId: 40

E/SingleTopActivity: onCreate hashCode: 254021317 taskId: 40

可以做个总结:

  • standard 和 singleTop 这两种模式下 taskAffinity 属性均不会生效,这两种模式启动的 Activity 总会尝试加入到启动者所在的任务栈中,如果启动者是 singleInstance 的话则会创建一个新的任务栈

  • standard 模式的 Activity 每次启动都会创建一个新的实例,不会考虑任何复用

  • singleTop 模式的 Activity 想要被复用,需要启动者所在的任务栈的栈顶就是该 Activity 实例

  • singleTask 模式的 Activity 事实上是系统全局单例,只要实例没有被回收就会一直被复用。singleTask 可以通过声明 taskAffinity 从而在一个特定的任务栈中被启动,且允许其它 Activity 一起共享同一个任务栈。如果不声明 taskAffinity 的话就会尝试寻找或者主动创建 taskAffinity 为 applicationId 的任务栈,然后在该任务栈中创建或复用 Activity

  • singleInstance 可以看做是 singleTask 的加强版,singleInstance 在任何时候都会独占一个任务栈,不管是否声明了 taskAffinity。在 singleInstance 任务栈中启动的其它 Activity 都会加入到其它任务栈中

需要注意的是,以上结论只适用于没有主动添加 Intent flag 的情况,如果同时添加了 Intent flag 的话就会出现很多奇奇怪怪的现象了

6、Intent flag

在启动 Activity 时,我们可以通过在传送给 startActivity(Intent) 方法的 Intent 中设置多个相应的 flag 来修改 Activity 与其任务栈的默认关联,即 Intent flag 的优先级会比 launchMode 高

Intent 提供的设置 flag 的方法有以下两个,一个是覆盖设置,一个是增量添加

private int mFlags;

public @NonNull Intent setFlags(@Flags int flags) {

mFlags = flags;

return this;

}

public @NonNull Intent addFlags(@Flags int flags) {

mFlags |= flags;

return this;

}

通过如下方式来添加 flag 并启动 Activity

val intent = Intent(this, StandardActivity::class.java)

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)

startActivity(intent)

如果 Activity 的启动模式只由 launchMode 定义的话,那么在运行时 Activity 的启动模式就再也无法改变了,相当于被写死了,所以 launchMode 适合于那些具有固定情景的业务。而 Intent flag 存在的意义就是为了改变或者补充 launchMode,适合于那些大部分情况下固定,少数情况下需要动态进行变化的场景,例如在某些情况下不希望 singleInstance 模式的 Activity 被重用,此时就可以通过 Intent flag 来动态实现

而这也造成了 Intent flag 很难理清楚逻辑,因为 Intent flag 往往需要组合使用,且还需要考虑和 launchMode 的各种组合配置,两者并不是简单的进行替换

Intent flag 有很多个,比较常见的有四个,这里就简单介绍下这几种 Intent flag

  • FLAG_ACTIVITY_NEW_TASK

  • FLAG_ACTIVITY_SINGLE_TOP

  • FLAG_ACTIVITY_CLEAR_TOP

  • FLAG_ACTIVITY_CLEAR_TASK

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_NEW_TASK 应该是大多数开发者最熟悉的一个 flag,比较常用的一个场景就是用于在非 ActivityContext 环境下启动 Activity。Android 系统默认情况下是会将待启动的 Activity 加入到启动者所在的任务栈,而如果启动 Activity 的是 ServiceContext 的话,此时系统就不确定该如何存放目标 Activity 了,此时就会抛出一个 RuntimeException

java.lang.RuntimeException: Unable to start service github.leavesc.launchmode.MyService@e3183b7 with Intent { cmp=github.leavesc.demo/github.leavesc.launchmode.MyService }: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

从异常信息可以看出此时 Intent 需要添加一个 FLAG_ACTIVITY_NEW_TASK 才行,添加后 Activity 就可以正常启动了

val intent = Intent(this, StandardActivity::class.java)

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

startActivity(intent)

FLAG_ACTIVITY_NEW_TASK 也有一个隐含的知识点,上文有讲到 standard 和 singleTop 这两种模式下 taskAffinity 属性均不会生效,但这个结论也只适用于没有主动添加 Intent flag 的情况

FLAG_ACTIVITY_NEW_TASK 和 standard 模式的组合情况可以总结为:

  • standard 没有设置 taskAffinity。此时系统就会去复用或者创建一个默认任务栈,然后直接在该任务栈上新建一个 Activity 实例入栈

  • standard 有设置 taskAffinity。此时又可以分为当前系统是否存在 taskAffinity 关联的任务栈两种情况

  • 不存在目标任务栈。此时系统就会创建目标任务栈,然后直接在该任务栈上新建一个 Activity 实例入栈

  • 存在目标任务栈。此时系统会判断任务栈中是否已经存在目标 Activity 的实例,如果不存在的话则新建一个 Activity 实例入栈。如果存在目标实例的话,则只是将该任务栈转到前台而已,既不会新建 Activity 实例,也不会回调 onNewIntent方法,甚至也不管该 Activity 实例是否处于栈顶,总之只要存在相同实例就不做任何响应。。。。。。。

可以看到,FLAG_ACTIVITY_NEW_TASK 的语义还是有点费解的,该标记位可以使得 taskAffinity 生效,创建或者复用任务栈并将其转到前台,但并不要求必须创建一个新的 Activity 实例,而是只要 Activity 实例有存在即可,而且也无需该 Activity 实例就在栈顶

FLAG_ACTIVITY_SINGLE_TOP

FLAG_ACTIVITY_SINGLE_TOP 这个 flag 看名字就很容易和 singleTop 联系在一起,实际上该 flag 也的确起到了和 singleTop 相同的作用

只要待启动的 Activity 添加了该标记位,且当前任务栈的栈顶就是目标 Activity,那么该 Activity 实例就会被复用,并且回调其onNewIntent方法,即使该 Activity 声明了 standard 模式,这相当于将 Activity 的 launchMode 覆盖为了 singleTop

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_CLEAR_TOP 这个 flag 则是起到了清除目标 Activity 之上所有 Activity 的作用。例如,假设当前要启动的 Activity 已经在目标任务栈中了,那么设置该 flag 后系统就会清除目标 Activity 之上的所有其它 Activity,但系统最终并不一定会复用现有的目标 Activity 实例,有可能是销毁后再次创建一个新的实例

看个例子。先以不携带 flag 的方式启动 StandardActivity 和 SingleTopActivity,此时日志信息如下

E/StandardActivity: onCreate hashCode: 76763823 taskId: 39

E/SingleTopActivity: onCreate hashCode: 217068130 taskId: 39

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

题外话

我们见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了7、8年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。

其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。

不断奔跑,你就知道学习的意义所在!

注意:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

共同特征:可能工作了7、8年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。

其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。

不断奔跑,你就知道学习的意义所在!

注意:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-bFFwhOQ1-1711535469442)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值