• Android -- 每日一问:Activity的启动模式(launchMode)有哪些,有什么区别?


    在这里插入图片描述

    经典回答

    这应该是一道很虐人的面试题,很多人都答不上来,很多人根本就没有用过。当我发现在被我面试的人中有80%的比例对它不了解时,我找过一些同事讨论是否还有在面试中考查这个问题的必要,得到的回答是“程序员何苦为难程序员”!

    因为很多程序员都认为这个启动模式没有多大用处。好吧,我用一个实际中很容易遇到的问题来引出它有多么有用。

    很多人在使用 startActivityForResult 启动一个 Activity 时,会发现还没有开始界面跳转本身的
    onActivityResult 马上就被执行了,这是为什么呢?

    遇到过吧,我见过很多人为了这个问题抓耳挠腮的。在 Activity.java 的 startActivityForResult 方法上看一下官方的说明吧:

         * <p>Note that this method should only be used with Intent protocols
         * that are defined to return a result.  In other protocols (such as
         * {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
         * not get the result when you expect.  For example, if the activity you
         * are launching uses the singleTask launch mode, it will not run in your
         * task and thus you will immediately receive a cancel result.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    很多人出现这个问题,确实是因为 startActivityForResult 启动的 Activity 设置了 singleTask 的启动模式。但是,除了这种情况还有可能会马上执行吗?

    有,而且很多。如下面表格,左边第1列代表 MainActivity 的启动模式,第一行代表 SecondActivity(即要 startActivityForResult 启动的 Activity)的启动模式,打叉代表在这种组合下 onActivityResult 会被马上调用。

    standsingleTopsingleTasksingleInstance
    xx
    xx
    xx
    xxxx

    好在幸运的是,Android 在 5.0 及以后的版本修改了这个限制。也就是说上面x的地方全部变成了√。

    那么在Android 5.0后,还会有这个问题吗?

    还是会的。如在 Intent 中设置了 FLAG_ACTIVITY_NEW_TASK再startActivityForResult ,即使是标准的启动模式仍然会有这个问题。

    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    
    • 1

    Log如下:

    I/MainActivity: onCreate
    I/MainActivity: onResume
    I/MainActivity: onPause
    I/MainActivity: onActivityResult requestCode=1 resultCode=0
    I/MainActivity: onResume
    I/MainActivity: onPause
    I/SecondActivity: onCreate
    I/SecondActivity: onResume
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:MainActivity的onResume 也会被触发。因为onActivityResult
    被执行时,它会重新获得焦点。很多人也会遇到 onResume 被无故调用,也许就是这种情况。

    所以,最终我们发现只要是不和原来的Activity在同一个 Task 就会产生这种立即执行 onActivityResult 的情况,从原代码也可以得到验证,详情查看ActivityStackSupervisor.java

            if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
                    && r.resultTo.task.stack != null) {
                // For whatever reason this activity is being launched into a new
                // task...  yet the caller has requested a result back.  Well, that
                // is pretty messed up, so instead immediately send back a cancel
                // and let the new task continue launched as normal without a
                // dependency on its originator.
                Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
                r.resultTo.task.stack.sendActivityResultLocked(-1,
                        r.resultTo, r.resultWho, r.requestCode,
                        Activity.RESULT_CANCELED, null);
                r.resultTo = null;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    原因

    其实上面代码中的英文注解也说得很清楚了,Android 认为不同的Task之间对这种要求返回结果的启动方式会产生一些依赖(对Task),所以干脆简单粗暴在跳转前直接返回 RESULT_CANCELED 结果。

    我们还是用一个例子简单解释一下,如下图,有两个任务栈(stack),处于前可视状态的是“Back Stack”也叫返回栈,处理后台的是“Background Task”。

    image.png

    当“Activity 2”通过 startActivityForResult 启动“Activity Y”时,“Background Task”中的Activity会被压入返回栈的栈顶。这种情况下,如果没有在跳转前直接返回 RESULT_CANCELED 给“Activity 2”,那么按Back键,应该要跳转到“Activity X”,而按Back键“Activity Y”就会调用finish会发送Result给启动它的“Activity 2”。这时就很难搞清楚,到底是“Activity 2”还是“Activity X”应该获得焦点了,会产生一些混乱或是违反的原有的一些约定。

    你的朋友是不是也在准备面试呢?你可以“请朋友读”,把今天的题目分享给好友,或许你能帮到他。

  • 相关阅读:
    【Transformers】预训练模型使用工具Transformer(1):初识Hugging Face
    虚拟内存相关笔记
    【慕课C#学习笔记】第二章: C#基础语法
    【Linux进程间通信】共享内存
    QT之OpenGL 计算机图形学线性代数基本知识
    Linux网络环境配置:(内含:随机ip和固定ip设置方式)
    Git下载安装及环境配置,解决安装包下载慢问题(详细版)
    数据治理案例 | 某大型集成电路企业数据质量管理实践
    【附源码】计算机毕业设计JAVA药品管理系统演示录像 2021
    【python学习】枚举算法案例分析
  • 原文地址:https://blog.csdn.net/duoduo_11011/article/details/128031466