查看原文
其他

每日一问:一般的 Context 启动 Activity 可以不加 FLAG 么?

nanchen nanchen 2020-10-29

最近经历了一段低谷期,各种各样的琐事铺天盖地的到来,我也第一次希望是这辈子唯一的一次在水滴筹上发起了众筹。不过通过这次的事情也终于发现,人间有爱,我们都应该常怀感恩。

无论是掘金老站长在掘金无偿地直接置顶推荐我的水滴筹文章,抑或是鸿洋在 wanAndroid 上的置顶推荐,还是各种不知名的读者和陌生朋友们的支持赞助,真的非常感谢,我唯有用尽全力,写出更加有深度和细节的文章才能回报大家吧。

当然还得感谢现实中的朋友们,有超级多的细节,让我感动涕零,数次差点掉下眼泪。各种细节,我终将铭记。

今天的文章,其实非常简单,也是自己无意中的发现。虽然可能无关紧要,没有任何骚操作,但还是想分享给大家。

想必大多数人都知道在我们使用非 Activity 的 startActivity() 的时候,都需要指定 Intent.FLAG_ACTIVITY_NEW_TASK,如果没有指定,直接进行操作则会直接抛出异常。

上面我们使用 applicationContext 做 startActivity() 操作,不出意外的引发了崩溃,而正确的代码是:

val intent = Intent(this, Main2Activity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
applicationContext.startActivity(intent)

这本身并没有什么值得争议的地方,但真的不加这个 FLAG,在手机上就一定会发生崩溃么?实际上,不加 FLAG 的处理也并不一定在手机上发生上述崩溃。

applicationContext.startActivity(Intent(this, Main2Activity::class.java))

上述的代码,有明显的问题,我们使用 applicationContext 来做 startActivity() 操作,却没有指定任何的 FLAG,但是,在 8.0 的手机上,你一定会惊讶的发现,我们并没有等到意料内的崩溃日志,而且跳转也是非常正常,这不由得和我们印象中必须加 FLAG 的结论大相径庭。然后再拿一个 9.0 的手机来尝试,马上就出现了上面的崩溃。

这是为什么呢?我们必须看看源码。我们先基于 SDK 26,直接打开 Context 的实现类 ContextImpl,直接通过关键字 context requires the FLAG_ACTIVITY_NEW_TASK flag 搜索定位到下面的方法。

当然,这里其实也可以直接一层一层跟进源码找到这个方法,效果一样。只是既然我们都通过日志知道了异常 message,那么直接通过异常关键字搜索一定是最快的,这个检索方法在很多时候非常有用!

@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);
}

然后我们再基于 SDK 28 打开源码:

@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. A bug was existed between N and O-MR1 which allowed this to work. We
// maintain this for backwards compatibility.
final int targetSdkVersion = getApplicationInfo().targetSdkVersion;

if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& (targetSdkVersion < Build.VERSION_CODES.N
|| targetSdkVersion >= Build.VERSION_CODES.P)
&& (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);
}

注释已经写的很清楚了,我们使用 Context.startActivity() 的时候是一定要加上 FLAG_ACTIVITY_NEW_TASK 的,但是在 Android N 到 O-MR1,即 24~27 之间却出现了 bug,即使没有加也会正确跳转。

对比源码发现,在我们非 Activity 调用 startActivity() 的时候,我们这个 options通常是 null 的,所以在 24~27 之间的时候,误把判断条件 options == null 写成了options != null 导致进不去 if,从而不会抛出异常。

关于启动模式的话,之前在 面试系列(可以在底部菜单找到) 已经对这一块知识点进行详细讲述,可直接点击链接前往:Android 面试:说说 Android 的四种启动模式

—————END—————



我是南尘,只做比心的公众号,欢迎关注我。

推荐阅读:

每日一问:SharedPreferences 那些不为人知的秘密(上)

每日一问:SharedPreferences 那些不为人知的秘密(下)

每日一问:谈谈自定义 View 都有些什么注意点


欢迎关注南尘的公众号:nanchen
做不完的开源,写不完的矫情,只做比心的公众号,如果你喜欢,你可以选择分享给大家。如果你有好的文章,欢迎投稿,让我们一起来分享。
          长按上方二维码关注        做不完的开源,写不完的矫情        一起来看 nanchen 同学的成长笔记



    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存