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参数即可

更多相关文章

  1. C语言函数的递归(上)
  2. Android(安卓)设计原则【+整理】
  3. android连接webapp发送接收消息最简实现
  4. Android中的Activity之间传递自定义类型的List的方法
  5. android中的常见类(二)
  6. android jni 的编写二 (NDK 开发中动态注册Jni)
  7. android GPS定位系统
  8. 如何在Mac上开发Android应用(原创,给刚接触android和mac的新人)
  9. 关于android

随机推荐

  1. 总结关于winfrom注意点
  2. 总结.Net MVC实现长轮询实例
  3. asp.net core实例教程之如何设置中间件
  4. 关于接口类型的10篇课程推荐
  5. 用微信PC端dll库实现截图的实例代码
  6. asp.net core实例教程之项目结构
  7. 有关ListView的文章推荐10篇
  8. .Net MVC+Data Table实现分页+排序的实例
  9. asp.net core实例教程之配置
  10. 有关DetailView的文章推荐3篇