最近在开发过程中遇到这么个问题,报错如下
当我使用传入context的Intent来启动Activity,app崩溃了,而且报了一个我以前重来没见过的错误。
错误log的意思大概为,我使用了一个不属于Activity的Context来调用startActivity方法,需要设置一个FLAG_ACTIVITY_NEW_TASK的Flag才可以正常运行,而且最后给我来个了疑问句,问我这是不是我所期待的也是挺骚的。
问题分析
(如果急于求解,不管过程和原因的话,可以跳过分析过程,直接看后面的 解决方法)
分析一下问题的原因,首先找到报错的代码,抛开这个context不说,这就是一个很普通的启动Activty,所以要着重看一下这个Context是哪里来的。
注:我所要启动的Activity是在Manifest中已经标记为栈内复用了
按住Ctrl左键点击context查看来源,发现这是从外部传进来的,找到调用的地方则发现传入的Context是BroadcastReceiver的Context,这下问题就来了,为什么四大组件之一 广播的context会引起这样错误?
这时候问题又变成了:BroadcastReceiver的Context和Activity的Context有什么不一样吗?
又到了需要看源码的时候了。。。
Ctrl 左键点击查看BroadcastReceiver的onReceive方法,追踪到源码部分
@SuppressWarnings("WeakerAccess") /* synthetic access */
void executePendingBroadcasts() {
while (true) {
final BroadcastRecord[] brs;
synchronized (mReceivers) {
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i=0; i<brs.length; i++) {
final BroadcastRecord br = brs[i];
final int nbr = br.receivers.size();
for (int j=0; j<nbr; j++) {
final ReceiverRecord rec = br.receivers.get(j);
if (!rec.dead) {
rec.receiver.onReceive(mAppContext, br.intent); //在这里,onReceive方法传入了mAppContext
}
}
}
}
}
继续查看mAppContext是如何赋值的,
private LocalBroadcastManager(Context context) {
mAppContext = context; //当初始化LocalBroadcastManager调用这个构造函数时,会将传入的Context赋值给这个mAppContext
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
找到调用构造函数的地方,就会发现传入的Context是通过getApplicationContext()获得的,也就是说我最终在广播BroadcastReceiver的onReceive(Context context, Intent intent)方法内获得的Context是通过调用context.getApplicationContext()方法获得的。
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext()); //调用LocalBroadcastManager的构造函数,传入的Context是通过getApplicationContext()获得的
}
return mInstance;
}
}
看到了这里,捋一下思路,我们广播BroadcastReceiver的onReceive方法中得到的Context是通过调用LocalBroadcastManager构造函数,传入的context.getApplicationContext()得到的,就是说我们通过广播得到的Context是Application的Context而不是Activity的Context,所以问题的原因一下就找到了,正因为我在广播内,使用了广播的Context来startActivity,所以会报出这个错误。
注:不仅广播,在Service服务内使用服务的Context来启动Activity亦然如此。
这下以后都会记住广播内onReceive()方法内传入的Context是Application的Context了,一轮下来也是有所收获,又是一个小细节。
解决方法
分析过后,就要着重于解决问题,大概有两种方法来解决这个问题:
- 给你要启动Activity的Intent添加一个FLAG_ACTIVITY_NEW_TASK的Flag(个人不推荐)
- 使用Activity的Context来调用该方法
1、以我的代码为例子,给Intent添加Flag,如下:
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
但是不要盲目地使用这个方法,因为设置这个flag会影响你的Activity任务栈,这可能会影响以后Activity关闭和开启,导致Activity达不到你想要的启动和关闭效果。
对于非Activity启动的Activity(比如Service或者通知中启动的Activity)需要显式设置Intent.FLAG_ACTIVITY_NEW_TASK,而singleTask及singleInstance在AMS中被预处理后,隐形的设置了Intent.FLAG_ACTIVITY_NEW_TASK,而启动模式是standard及singletTop的Activity不会被设置Intent.FLAG_ACTIVITY_NEW_TASK,除非通过显式的intent.setFlag进行设置。
可以看官方文档的介绍:https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK
但是官方文档的介绍也不一定好理解,要更好地理解Activity的启动方式,个人推荐看看这篇博客:https://www.jianshu.com/p/b3a95747ee91
2、不在Service,BroadcastReceiver内获取Context启动Activity,使用外部传入的Activity的Context来启动
方法有很多,调用时传入方法或者类以外Activty的Context,或者使用全局获取的Activity的Context(类似定义一个公共静态方法来专门获取公共的Context)。
共勉!