首先说说 android跳转scheme的代码如下
String url = "fungo:xxxxxx";
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse(url));
startActivity(intent);
通过捕捉可以发现OPPO A57(android 6.0.1)手机在控制台中打印如下错误信息
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.Is this really what you want?
为什么不同型号的手机有的会出现有的不会出现?最综定位如下:
在执行在执行startActivity这个函数的时候需要依赖Context对象,如果Context刚好是Activity对象,那恭喜你,执行不会出现问题,如果不是activity,而是系统的Context那就有可能会出现问题了,特别是当我们封装跳转startActivity的函数的时候,参数往往是Context,下面的函数不是很熟悉, 这个时候如果将该函数作为 公共方法,那就特别容易出问题了,加上开发人员的测试手机刚刚是android7-8系统的,所以问题就被埋没了。
public void startActivity(Context contet,Uri uri){
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse(url));
startActivity(intent);
}
说一下定位过程,知道startActivity函数的问题,那就要看不同系统版本内部该函数的实现逻辑,通过定位,可以发现startActivity真正的实现类是ContextImpl
如下android 7.0-android 8.0系统startActivity的源码,可以发现下面这个判断 跳转scheme,跳转scheme我们调用的是startActivity(intent);options参数是传递null,按照下面的判断可以知道,即使我们使用的系统的Context,也不会进逻辑判断里面去,因为如果option参数为null的时候,是不满足下面的逻辑判断的,这就是为什么android 7.0-android 8.0能正常跳转的问题
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
}
//android 7.0-android 8.0系统startActivity的内部实现
@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);
}
再看看android 6.0.1以下的逻辑判断,仅仅是下面的判断,使用的系统的Context 刚好满足所以会出错
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {}
//android 6.0 系统startActivity的内部实现
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);
}
最后看看android 9.0.0的源码,对比android 7.0-8.0的可以很快的发现增加了options为null的判断,叉叉,怪不得9.0的手机也会出现
options == null|| ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)
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?");
}
至此,总结基本完毕。
在新项目中,遇到这类问题的可能比较少,但是如果是一个老项目,并且经过n次迭代,并将函数封装到公共库中,新人调用的话,就会遇到了,其实最好的做法就是对context参数进行instanceof判断,如果content是Activity对象,可以不用添加FLAG_ACTIVITY_NEW_TASK,如果不是则添加FLAG_ACTIVITY_NEW_TASK参数即可