当前位置:网站首页>在Activity外使用startActivity()方法报错原因与解决办法
在Activity外使用startActivity()方法报错原因与解决办法
2022-06-29 09:13:00 【枫林梦】
相信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。
来源:历史课代表
边栏推荐
- 转载 :判断对象是否具有属性的5种方法
- Leetcode skimming -- teponacci sequence
- 遍历vector容器中的对象的方式
- Gross Tumor Volume Segmentation for Head and Neck Cancer Radiotherapy using Deep Dense Multi-modalit
- Making of simple addition calculator based on pyqt5 and QT Designer
- 云管理平台:9大开源云管理平台(CMP)
- 长安链数据存储介绍及Mysql存储环境搭建
- 券商经理给的开户二维码办理股票开户安全吗?我想开个户
- 基于keil5自动配置stm32f103标准库的官网freertos移植
- 基於PyQt5和Qt Designer的簡易加法計算器的制作
猜你喜欢

Do you know what BFD is? This article explains the principle and usage scenarios of BFD protocol in detail

CROSSFORMER: A VERSATILE VISION TRANSFORMER BASED ON CROSS-SCALE ATTENTION

kdevelop新建工程

Matlab tips (21) matrix analysis -- partial least squares regression

Deep Learning-based Automated Delineation of Head and Neck Malignant Lesions from PET Images
![[Huawei certification] the most complete and selected question bank in hcia-datacom history (with answer analysis)](/img/d4/f5ea847573433f7ca7bd429f57e40a.png)
[Huawei certification] the most complete and selected question bank in hcia-datacom history (with answer analysis)

Student增删gaih

1424. 对角线遍历 II

IPC(进程间通信)之管道详解

转载 :判断对象是否具有属性的5种方法
随机推荐
Data visualization: the significance of data visualization
Data governance: Metadata Management (Part 2)
User level threads and kernel level threads
Is it safe to open an account for stock speculation? Is it reliable?
linux环境下安装配置redis,并设置开机自启动
Closed training (25) basic web security
Lc236. nearest common ancestor of binary tree
Closed door cultivation (24) shallow understanding of cross domain problems
The 23 most useful elasticsearch search techniques you must know
数据治理:数据标准管理(第三篇)
请用已学过的知识编写程序,找出小甲鱼藏在下边这个长字符串中的密码,密码的埋藏点符合以下规律:
367. 有效的完全平方数-二分法
LC236. 二叉树的最近公共祖先
完美二叉树、完全二叉树、完满二叉树
基于keil5自动配置stm32f103标准库的官网freertos移植
1424. diagonal traversal II
Data governance: the solution of data governance in the data Arena
基于stm32标准库独立按键的多按键状态机的实现
Please use the learned knowledge to write a program to find out the password hidden in the long string below. The burial point of the password conforms to the following rules:
微信小程序重写Page函数,实现全局日志记录