startActivity兼容性问题总结
startActivity兼容性问题总结
最近事情比较多,也遇到了很多坑,特别是最近android不同手机跳转scheme兼容性问题,在测试中发现android 6.0.1以下的手机不可以正常跳转,7.0-8.0的系统可以正常跳转,9.0的系统又无法正常跳转,为了找出真正的内部原因,今天特意查看相关的源代码
首先说说 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参数即可
更多相关文章
- C语言函数的递归(上)
- Android(安卓)设计原则【+整理】
- android连接webapp发送接收消息最简实现
- Android中的Activity之间传递自定义类型的List的方法
- android中的常见类(二)
- android jni 的编写二 (NDK 开发中动态注册Jni)
- android GPS定位系统
- 如何在Mac上开发Android应用(原创,给刚接触android和mac的新人)
- 关于android