美文网首页
非activity环境启动activity还需要FLAG_ACT

非activity环境启动activity还需要FLAG_ACT

作者: 普通的程序员 | 来源:发表于2018-05-13 12:29 被阅读0次

2018-11-7更新
更新先说结果

原理上按照设计,service,application,broadcast不能直接startActivity,除非加上FLAG_NEW_TASK

但是api 23 ,也就6.0以下的遵循了该设计原则

api 24,25,26,27官方出现了bug,不加flag也能正常启动,bug就是 &&option!=null

api 28 解决了该笔bug

背景

之前有做一个保活业务,另一个进程内,开启一个service启动notification。
参考Android保活/拉活代码实现笔记

最近要求做一个应用截屏分享,这些方案网上都有,不赘述。
最终我的实现方案
Android 应用监听截屏操作

当时我不想在baseActivity内做监听注册/反注册。
加上提高拉活效果的考虑,我决定把这个监听放在保活service里
(乖孩子app不要这样做,坏宝宝这样做实际效果真的很流氓)

当监听到有截图内容的变化,此时service内启动一个 截屏分享的activity,我开发的时候用的是 7.0的机器,自测通过。

Bug情况

可是交付测试的时候,部分机器发生崩溃,崩溃log如下

UncaughtException detected: android.util.AndroidRuntimeException: 
Calling startActivity() from outside of an Activity 
context requires the FLAG_ACTIVITY_NEW_TASK flag. 
Is this really what you want?

ok,log已经很清楚了
原因就是

(这段没说清,我回头看,自己都看不懂)

activity继承了context重载了startActivity方法,如果使用acitvity中的startActivity,不会有任何限制。

而如果直接使用context的startActivity则会报上面的错误,根据错误提示信息,可以得知,如果要使用这种方式需要打开新的TASK。

但是7.0之后,不需要这样
当时为了验证这个结论,特地在业务代码里加了一段统计上报。
catch到这个exception就 加上这个flag,
大概收集了1w+的信息,没有一条是7.0以上的机器。

真正的源码阅读

结合
https://mp.weixin.qq.com/s/AUKoLpGrCyJd1YcPGP7Yyw
完成context的源码阅读(文章不一定全对)

1.首先有个结论,activity,service,application的大部分实现都是contextImpl里的,比如getAsset;

2.但是启动startActivity需有任务栈的概念,application/service启动activity没有任务栈,所以必须要new_task_flag新建一个任务栈,这里从源码上去看,非acivity启动场景,直接走contextImpl,activity场景进行了复写。

从堆栈信息进行验证(我是故意让写了一个不存在的action到intent里,使得startActivity报错)

从service启动activity 从activity启动activity

3.但是矛盾的是,在standard的启动模式下,Android 24以上,service启动activity死活不会抛出这个异常。
原因可能是Android 24以上
contextImpl的startActivity多了getLaunchTaskId这样一个判断

实际上是option为null

是api 24,25,26的bug!

27已经修复!

https://mp.weixin.qq.com/s/I-raDDL8Zben5rzWf0dTNQ

@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
        // generally not allowed, except if the caller specifies the task id the activity should
        // be launched in.
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

当时也以为是我写的service运行在另一个进程里,所以会必须要求用new_task。现在看来,跟另一个进程没关系,

结论

api 24以下,非activity场景启动activity必须加new_task,原因还是任务栈的概念。
api 24及以上,非activity场景启动activity不加new_task不会抛出异常(原因可能是taskId的判断),而且做了new_task的操作,因为后续继续启动的activity也都是正常的,标记可加可不加。

相关文章

网友评论

      本文标题:非activity环境启动activity还需要FLAG_ACT

      本文链接:https://www.haomeiwen.com/subject/kmtarftx.html