在Activity外使用startActivity()方法报错原因与解决办法
作者:互联网
相信Android开发都遇到过这样一个报错信息
04-09 15:55:08.165: E/AndroidRuntime(3403): android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
解决办法也很简单,就是在非Activity的Context调用startActivity方法时,Intent添加一个FLAG_ACTIVITY_NEW_TASK的flag,代码如下
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
但是细心的同学会发现Android7.0及以上的机型即使没有添加以上代码,也不会崩溃。这是怎么回事呢?
这就涉及到activity的startActivity和context的startActivity方法之间的区别,Activity也是Context的子类,但是Activity本身实现了startActivity方法,所以两个是不同的。
activity的startActivity方法最终会调用Activity类的startActivityForResult方法,代码如下
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
context的 startActivity方法最终会调用ContextImpl的startActivity方法,代码如下
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
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);
}
7.0及以下
@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);
}
从上面代码可以看出,Activity的startActivity方法是不会抛出文章开始说的那个错的;问题集中在非Activity的Context调用startActivity方法,在7.0之前如果Intent没有添加flag:FLAG_ACTIVITY_NEW_TASK,程序是必崩的,7.0及以上如果调用startActivity(intent)方法,是不会崩的,因为此时options为空。一般为了兼容7.0以下版本,我们还是得添加flag的。
startActivity(intent, null);
注意7.0增加了两个判断条件
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?");
}
到这里基本上文章就该结束了,但是我们项目在线上还是遇到文章开始说的那个问题,有一些崩溃,都是7.0以下的机型。经过一番寻找,终于定位到有问题的代码,原因是我们用到了Intent的一个方法createChooser(Intent target, CharSequence title)
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, text);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
shareIntent.setType("text/plain");
Utils.getContext().startActivity(Intent.createChooser(shareIntent, "分享到"));
从代码上看到我们明明给Intent添加了flag,还是会崩溃
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
我们跟进Intent.createChooser方法里看看
public static Intent createChooser(Intent target, CharSequence title, IntentSender sender) {
Intent intent = new Intent(ACTION_CHOOSER);
intent.putExtra(EXTRA_INTENT, target);
if (title != null) {
intent.putExtra(EXTRA_TITLE, title);
}
if (sender != null) {
intent.putExtra(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, sender);
}
// Migrate any clip data and flags from target.
int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION
| FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| FLAG_GRANT_PREFIX_URI_PERMISSION);
if (permFlags != 0) {
ClipData targetClipData = target.getClipData();
if (targetClipData == null && target.getData() != null) {
ClipData.Item item = new ClipData.Item(target.getData());
String[] mimeTypes;
if (target.getType() != null) {
mimeTypes = new String[] { target.getType() };
} else {
mimeTypes = new String[] { };
}
targetClipData = new ClipData(null, mimeTypes, item);
}
if (targetClipData != null) {
intent.setClipData(targetClipData);
intent.addFlags(permFlags);
}
}
return intent;
}
首先这个方法先把原Intent存到新Intent的extra上,
intent.putExtra(EXTRA_INTENT, target);
然后permFlags值为0,就这样,新Intent的flag就没了,所以返回来的Intent的flag值为0,虽然extra里的Intent的flag值为Intent.FLAG_ACTIVITY_NEW_TASK,但是没有,取flag值时不是从extra里的Intent取值。
int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION
| FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| FLAG_GRANT_PREFIX_URI_PERMISSION);
有点坑吧,所以如果使用了Intent.createChooser()方法后还得加上
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
Utils.getContext().startActivity(Intent.createChooser(shareIntent, "分享到").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
建议以后如果有非Activity的Context调用startActivity()方法,一定要用7.0以下的机型测试一下,我们手上的测试大部分是7.0的,加上那段代码的使用场景很少,所以导致了线上出现这个bug。
来源:历史课代表
标签:null,startActivity,ACTIVITY,FLAG,Intent,Activity,intent,报错 来源: https://blog.csdn.net/weixin_42685006/article/details/95749604