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报错)


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也都是正常的,标记可加可不加。
网友评论