Activity 的启动:四种启动模式及各种 FLAG

启动模式简介

Activity 有四种不同的启动模式,这四种模式分别是:standardsingleTopsingleTasksingleInstance

这四种模式中,standard模式是默认的模式,其他三个想要使用的话,要在 AndroidMainFest 文件中进行修改(通过给对应的 activity 设置 launchMode 属性,例如:)。

预备知识

  1. 任务栈(Task Stack) 介绍(任务栈也叫返回栈(Back Stack)):

    1. 任务栈用于存放用户开启的 Activity
    2. 在应用程序创建之初,系统会启动一个任务栈(默认一个),并存储根 Activity
    3. 新启动的 Activity 将会位于栈顶
    4. 当任务栈的最后一个 Activity 被销毁时,将清除任务栈并退出程序,下次再进入程序时会创建新的任务栈
  2. taskAffinity 介绍:

    1. taskAffinity 是 Activity 的一个属性,可在 Manifest 文件中设置。
    2. Task 也有该属性,它的值由第一个入栈的 Activity 决定
    3. Application 也有 taskAffinity 属性,它的值为 Manifest 的包名
    4. 默认情况下(没有显示设置 Activity 的 taskAffinity),所有 Activity 的 taskAffinity 属性都从 Application 继承,也就是说所有 Activity 的 taskAffinity 值都相同,为包名。
    5. taskAffinity 的值应该是 xxx.xxx.xxx 这种样式,如果只是普通的字符串 xxx,是安装不了应用的。
  3. 如何在代码中获取 Activity 的 taskAffinity 属性值和 Activity 所在 Task:

    // 当前 Activity 的 taskAffinity 属性值 
    String taskAffinity = "";
    try {
        ActivityInfo activityInfo = getPackageManager().getActivityInfo(getComponentName(),
                PackageManager.GET_META_DATA);
        taskAffinity = activityInfo.taskAffinity;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    // 当前 Activity 所在 Task
    int taskId = getTaskId();

四种模式

概述

  • standard:默认的启动方式。在该模式中,每启动一个新活动,都会创建一个新的实例,并在任务栈中入栈,处于栈顶的位置。假设你不停地进入同一个活动,你点击了十次,就要返回十次才能退出程序,因为你在返回栈中创造了十个相同的实例,尽管活动是一样的。
  • singleTop:启动一个新活动时,系统会检查该活动是否已经位于任务栈栈顶,如果是的话直接使用已存在的栈顶活动,否则就创建新活动并压入栈顶。
  • singleTask:和 singleTop 类似,不过这里是检查整个任务栈的活动,如果发现已经存在该活动就将位于该活动上方的活动全部出栈,该活动成为新的栈顶。
  • singleInstance:最特殊的模式,系统为该模式的活动分配一个独立的任务栈,该任务栈有且只有一个该活动实例。也就是说,如果已经创建过目标活动实例,那么将不会创建新的任务栈,而是唤醒之前创建过的活动实例。

活动生命周期的变化

  1. 在 standard 模式下,启动一个新活动时,旧活动和新活动依次执行的生命周期方法如下:
2019-09-13 14:56:58.287 9848-9848/com.feng.startoptimizationtest D/fzh: onPause: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 14:56:58.346 9848-9848/com.feng.startoptimizationtest D/fzh: onCreate: com.feng.startoptimizationtest.FirstActivity@4511a17, run
2019-09-13 14:56:58.351 9848-9848/com.feng.startoptimizationtest D/fzh: onStart: com.feng.startoptimizationtest.FirstActivity@4511a17, run
2019-09-13 14:56:58.353 9848-9848/com.feng.startoptimizationtest D/fzh: onResume: com.feng.startoptimizationtest.FirstActivity@4511a17, run
2019-09-13 14:56:58.840 9848-9848/com.feng.startoptimizationtest D/fzh: onStop: com.feng.startoptimizationtest.FirstActivity@162e105, run

可以看到,先执行了旧活动的 onPause,然后依次执行新活动的 onCreate,onStart 和 onResume,最后执行旧活动的 onStop

  1. 在 singleTop, singleTask 或 singleInstance 模式下,启动一个和当前栈顶活动相同的活动时,依次执行的生命周期方法如下:
2019-09-13 15:08:21.972 12508-12508/com.feng.startoptimizationtest D/fzh: onPause: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:08:21.973 12508-12508/com.feng.startoptimizationtest D/fzh: onResume: com.feng.startoptimizationtest.FirstActivity@162e105, run

可以看到,这时不会创建新的活动,而是先后执行当前活动的 onPause 和 onResume 方法

  1. FirstActivity 为 singleTask 模式,先启动 FirstActivity,然后启动 SecondActivity,最后再一次启动 FirstActivity,其中,最后一次启动 FirstActivity 涉及的生命周期方法如下:
2019-09-13 15:20:30.938 13922-13922/com.feng.startoptimizationtest D/fzh: onPause: com.feng.startoptimizationtest.SecondActivity@45089c1, run
2019-09-13 15:20:30.957 13922-13922/com.feng.startoptimizationtest D/fzh: onRestart: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:20:30.959 13922-13922/com.feng.startoptimizationtest D/fzh: onStart: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:20:30.960 13922-13922/com.feng.startoptimizationtest D/fzh: onResume: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:20:31.396 13922-13922/com.feng.startoptimizationtest D/fzh: onStop: com.feng.startoptimizationtest.SecondActivity@45089c1, run
2019-09-13 15:20:31.399 13922-13922/com.feng.startoptimizationtest D/fzh: onDestroy: com.feng.startoptimizationtest.SecondActivity@45089c1, run

可以看到,在 singleTask 模式下,如果任务栈已经有要启动的活动,则一次调用该活动的 onRestart, onStart 和 onResume,而在栈中位于该活动之上的其他活动将会被销毁(这里为 SecondActivity)

  1. FirstActivity 为 singleInstance 模式,先启动 FirstActivity,然后启动 SecondActivity,最后再一次启动 FirstActivity,其中,最后一次启动 FirstActivity 涉及的生命周期方法如下:
2019-09-13 15:38:51.995 15361-15361/com.feng.startoptimizationtest D/fzh: onPause: com.feng.startoptimizationtest.SecondActivity@51dbca3, run
2019-09-13 15:38:52.008 15361-15361/com.feng.startoptimizationtest D/fzh: onRestart: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:38:52.009 15361-15361/com.feng.startoptimizationtest D/fzh: onStart: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:38:52.011 15361-15361/com.feng.startoptimizationtest D/fzh: onResume: com.feng.startoptimizationtest.FirstActivity@162e105, run
2019-09-13 15:38:52.668 15361-15361/com.feng.startoptimizationtest D/fzh: onStop: com.feng.startoptimizationtest.SecondActivity@51dbca3, run

可以看到,由于 FirstActivity 位于不同的任务栈,并且该任务栈只会存在一个活动实例,所以重新启动 FirstActivity 时不会再次创建实例,而是复用之前的实例,所以会依次执行 FirstActivity 的 onRestart, onStart 和 onResume

添加 FLAG 后对 Activity 启动的影响

上面分析的启动模式都是静态设置的,也就是在 Manifest 文件中设置的,并且只分析了 Activity 启动 Activity 的情况,采用的是默认的 Intent,没有额外添加任何 FLAG。

下面就来分析一下给 Intent 设置了 FLAG 后,对 Activity 的启动由什么影响:

Intent.FLAG_ACTIVITY_NEW_TASK

  1. 该 FLAG 的作用:

这个 FLAG 跟 SingleInstance 有点相似,设置了该 FLAG 后,会检查是否存在与目标 Activity 的 taskAffinity 值相同的 Task,如果不存在的话就新建一个这样的 Task 并将目标 Activity 压入栈顶

要注意的是,默认情况下,同一个应用的所有 Activity 的 taskAffinity 值是相同的,为应用包名。所以如果没有在 Manifest 文件中将目标活动的 launchMode 属性设置为 singleInstance,那么启动新活动时添加了 Intent.FLAG_ACTIVITY_NEW_TASK 这个 FLAG 并不会产生影响,新活动还是在同一个栈中。

而如果没有设置该 FLAG 的话,即使两个 Activity 的 taskAffinity 值不一样,也不会新建一个 Task

  1. 和 SingleInstance 的区别:
  • Intent.FLAG_ACTIVITY_NEW_TASK 这个 FLAG 只是决定是否新建一个 Task,没有规定新的 Task 只能有一个活动实例,而 SingleInstance 模式下新的 Task 只能有一个活动实例
  • 在默认情况下,同一个应用的所有 Activity 的 taskAffinity 值相同,此 FLAG 不会创建新的 Task,而 SingleInstance 模式下会创建新的 Task
  1. 根据启动模式可分为两类:
  • singleTask 和 singleInstance 在 AMS 中被预处理后,隐性地设置了 Intent.FLAG_ACTIVITY_NEW_TASK 这个 FLAG
  • standard 和 singleTop 则需要显性地设置该 FLAG,即通过 Intent.setFlags 设置
  1. 非 Activity 启动 Activity 都必须添加 Intent.FLAG_ACTIVITY_NEW_TASK 才行。例如在 Service 中启动 Activity,需要这样写:
    Intent intent = new Intent(StartActivityService.this, FirstActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
    startActivity(intent);

如果没有添加该 FLAG 的话,就会抛出异常(据说在 Android 7.0 和 8.0 版本不会抛出异常,不过在 Android 9.0 已经修复了这个 bug)

为什么要这样限制呢?

如果某个活动不是通过 Activity 启动的,说明不是用户主动的行为,也就是说这个活动可能会出现在任何 APP 的活动之上,这时如果不用 Intent.FLAG_ACTIVITY_NEW_TASK 将这个活动限制在自己的 Task 中,就可能让用户误以为新的活动是属于当前 APP,这是不合理的。

  1. 有一种情况比较特殊,并不会启动新的 Activity:

如果新启动 Activity 的 taskAffinity 和当前 task 的该属性一致,此时如果新 Activity 的 Intent 和 当前 task 的根 Activity 的 Intent 一致的话,就不启动新 Activity,否则启动新 Activity。

下面用一张图来说明:(此图出处:https://www.jianshu.com/p/b3a95747ee91)

Intent.FLAG_ACTIVITY_CLEAR_TASK

  1. 首先要注意,Intent.FLAG_ACTIVITY_CLEAR_TASK 这个 FLAG 必须和 Intent.FLAG_ACTIVITY_NEW_TASK 配合使用,即:
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

单独使用 Intent.FLAG_ACTIVITY_CLEAR_TASK 是没有作用的。

  1. 两个 FLAG 配合使用的效果:

如果目标 Task 已存在,就先清空目标 Task(将目标 Task 原有的 Activity 全部出栈),然后新 Activity 称为目标 Task 的根 Activity。如果目标 Task 还不存在,就新建目标 Task,新 Activity 成为根 Activity(这和单独设置 Intent.FLAG_ACTIVITY_NEW_TASK 的效果一样)。

  1. 和启动模式的搭配:
  • 如果新 Activity 的 launchMode 为 standard, singleTop 或 singleTask,那么启动模式对 FLAG 没有影响,还是会清除原有 Task 的 Activity。
  • 如果新 Activity 的 launchMode 为 singleInstance,分两种情况:
    1. 新 Activity 没有创建过,那么该 FLAG 就不起作用,只有 singleInstance 其作用,新 Activity 成为一个新建 Task 的根 Activity,其他 Activity 不受影响。
    2. 新 Activity 已经创建过了,那么该 FLAG 就其作用了,目标 Task 中原有的实例会被销毁,然后新 Activity 成为目标 Task 的根 Activity。

Intent.FLAG_ACTIVITY_SINGLE_TOP

这个 FLAG 的效果和在 Manifest 文件设置 launchMode 为 singleTop 是一样的。

当然,它还可以和 Intent.FLAG_ACTIVITY_NEW_TASK,Intent.FLAG_ACTIVITY_CLEAR_TASK 等 FLAG 配合使用,从而产生和 singleTop 不一样的效果。

Intent.FLAG_ACTIVITY_CLEAR_TOP

  1. 单独使用 Intent.FLAG_ACTIVITY_CLEAR_TOP,没有设置 launchMode 的情况:

假如 Task 中由下往上依次有 A、B、C,这时 C 启动 B,并且带有 Intent.FLAG_ACTIVITY_CLEAR_TOP,产生的结果是:Task 中原有的 B,C 出栈,新创建的 B 入栈,成为新的栈顶。

也就是说单独使用这个 FLAG 时,无论新 Activity 是否已经存在于栈中,它都会被创建

  1. 和其它 launchMode 结合的情况:
  • 当新 Activity 的 launchMode 为 singleTop 时,和该 FLAG 结合后,作用相当于 singleTask。
  • 当新 Activity 的 launchMode 为 singleTask 或 singleInstance 时,该 FLAG 不起作用,由相应的 launchMode 主导。
  1. Intent.FLAG_ACTIVITY_CLEAR_TOP 和 Intent.FLAG_ACTIVITY_SINGLE_TOP 配合使用的情况:

由于 Intent.FLAG_ACTIVITY_SINGLE_TOP 相当于 launchMode 为 singleTop 的情况,所以由上面第二点可知,两者结合使用的效果就相当于 launchMode 为 singleTask。

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值