美文网首页典型bug积累
Android7.0更新升级功能bug分析

Android7.0更新升级功能bug分析

作者: 萧竹 | 来源:发表于2018-06-17 16:38 被阅读106次

1.背景

在工作上遇到了一个关于在Android7.0下载更新安装包无法安装的兼容性问题,特记录下来,作为典型兼容问题的积累和专业知识的积累,以及提醒自己在后续的测试工作中要遇到系统版本更新时,要做到系统性地分析版本特性,然后针对自身项目发起平台/系统升级的补充测试,提高测试覆盖率,保障产品质量。

2.问题描述

测试组里某个项目产品APP进行更新升级功能测试时,出现了一个问题,就是发现在自动更新功能的时候,下载好了apk的文件后不能自动跳到安装界面,导致无法安装相应的新版本,发现这个问题只会发生在Android 7.0版本的设备上,在较低版本的设备上则无这个问题。

3.问题引入原因分析

3.1 先来了解什么是APP更新升级功能

app在线更新是一个比较常见需求,新版本发布时,用户进入我们的app,就会弹出更新提示框,第一时间更新新版本app。在线更新分为以下几个步骤:

1, 通过接口获取线上版本号,versionCode
2, 比较线上的versionCode 和本地的versionCode,弹出更新窗口
3, 下载APK文件(文件下载)
4,安装APK

在线更新就上面几个步骤,前2步比较简单,重要的就是后2个步骤,而由于Android 各个版本对权限和隐私的收归和保护,因此,可能会出现各种的适配问题

3.2 理解安装APK的实现原理

上一节讲到由于Android 各个版本对权限和隐私的收归和保护,因此,下载和安装apk时可能会出现各种的适配问题,由于此bug是安装apk时出现的问题,所以我们就来重点分析一下安装apk的实现原理。

安装APK步骤

一般安装apk之前是先下载apk,一般最简单的方式是用 DownloadManager 来下载apk。

DownloadManager 是SDK 自带的,大概流程如下:
(1)创建一个Request,进行简单的配置(下载地址,和文件保存地址等)
(2)下载完成后,系统会发送一个下载完成的广播,我们需要监听广播。
(3)监听到下载完成的广播后,根据id查找下载的apk文件
(4)在代码中执行apk安装。

DownloadManager在下载完成之后,会发送一个下载完成的广播DownloadManager.ACTION_DOWNLOAD_COMPLETE,我们只需要监听这个广播,收到广播后, 获取apk文件安装。

定义一个广播DownloadReceiver:

class DownloadReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(final Context context, final Intent intent) {
        //  安装APK
        long completeDownLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);

        Logger.e(TAG, "收到广播");
        Uri uri;
        Intent intentInstall = new Intent();
        intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intentInstall.setAction(Intent.ACTION_VIEW);

        if (completeDownLoadId == mReqId) {
           uri = mDownloadManager.getUriForDownloadedFile(completeDownLoadId);
         }

         intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
         context.startActivity(intentInstall);
        }

    }

在下载之前注册广播

 // 注册广播,监听APK是否下载完成
  weakReference.get().registerReceiver(mDownloadReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

通过上面的几个步骤,基本上就完成app在线更新功能,在Android 6.0以下可以正常运行。

3.3 Android7.0系统 文件访问权限特性

每个Android版本的发布,对于安全性问题的要求越来越高,也为Android程序员增加了额外的工作量。
Android7.0引入私有目录被限制访问和StrictMode API 。

私有目录被限制访问是指在Android7.0中为了提高应用的安全性,在7.0上应用私有目录将被限制访问,这与iOS的沙盒机制类似。
StrictMode API是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,则会报出异常,也就是说不能访问你应用私有的文件夹了
带来的影响:这项权限的变更将意味着你无法通过File API访问手机存储上的数据了,基于File API的一些文件浏览器等也将受到很大的影响,例如文件下载安装、上传图片功能、系统相机拍照,或裁切照片功能等。

3.4 得出BUG引入原因

综上所述,我们理解了更新升级的功能和功能实现原理,以及通过对Android系统文件访问权限的特性的详细分析,得出在Android 7.0上,对文件的访问权限作出了修改,从代码中可以看出,Uri.fromFile导致我们在7.0上出现了问题,它其实就是生成一个file://URL。这就是为什么在下载完成后,无法进行自动安装,因为一旦我们通过这种办法打开系统安装器,就认为file:// URI类型的 Intent 离开我的应用,这样程序就会发生异常;
所以在Android7.0上,不能在使用file://格式的Uri 访问文件 ,Android 7.0提供 FileProvider,应该使用这个来获取apk地址,然后安装apk。

4.解决方案

解决方案那就是允许共享你私有目录下的一个文件夹,共享出去让大家访问,这样就可以访问你下载的apk来安装了,将使用FileProvider,它的步骤是:

  • 第一步:
    在AndroidManifest.xml中注册provider,provider可以向应用外提供数据。
<provider
   android:name="android.support.v4.content.FileProvider"
   android:authorities="你的包名.fileprovider"
   android:grantUriPermissions="true"
   android:exported="false">
   <meta-data
       android:name="android.support.FILE_PROVIDER_PATHS"
       android:resource="@xml/file_paths" />
</provider>
  • 第二步:
    在res/xml/file_paths.xml创建文件。 内容为:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path path="" name="download"/>
    </paths>
</resources>

这个要说明一下

<files-path/>代表的根目录: [Context.getFilesDir()](https://developer.android.com/reference/android/content/Context.html?hl=zh-tw#getFilesDir())

<external-path/>代表的根目录: [Environment.getExternalStorageDirectory()](https://developer.android.com/reference/android/os/Environment.html?hl=zh-tw#getExternalStorageDirectory())

<cache-path/>代表的根目录: [getCacheDir()](https://developer.android.com/reference/android/content/Context.html?hl=zh-tw#getCacheDir())

这样就把这个目录给共享出去了

  • 第三步:通过FileProvider获取URI进行安装成功
   if(Build.VERSION.SDK_INT>=24) {//判读版本是否在7.0以上
                    Uri apkUri = FileProvider.getUriForFile(this, "你的包名.fileprovider", apkFile);//在AndroidManifest中的android:authorities值
                    Intent install = new Intent(Intent.ACTION_VIEW);
                    install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    install.setDataAndType(apkUri, "application/vnd.android.package-archive");
                    startActivity(install);
   } else{
                    Intent install = new Intent(Intent.ACTION_VIEW);
                    install.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
                    install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(install);
  }

5.总结

将功能代码实现和系统平台特征分析结合起来,运用到测试实践中,还是蛮重要的,通过看别人的代码了解功能的实现方式,进而思考这种实现方式在各系统版本中可能会存在的适配问题,避免掉一些由于对系统对功能实现不了解而忽略的场景,使得测试覆盖率更高,更精准。

另外,一个困扰自己超过2个小时的问题有必要整理下来,这篇小文章不过用了30分钟整理,但是积累多了也是一份宝贵的财富。

相关文章

  • Android7.0更新升级功能bug分析

    1.背景 在工作上遇到了一个关于在Android7.0下载更新安装包无法安装的兼容性问题,特记录下来,作为典型兼容...

  • 蓝牙固件升级

    升级介绍 蓝牙固件升级是使用手机给固件进行更新,以达到修复bug,完善功能的作用。升级的大概流程是:首先,当手环的...

  • 腾讯Bugly java.lang.ClassNotFoundE

    在使用腾讯Bugly的应用升级功能时,遇到了如下Bug: 看了下Bugly的更新日志,最新的1.5.23版本的改动...

  • ionic2学习指引-如何升级app

    前言 ionic框架和ionic cli更新比较快.所以bug修复和新功能层出不穷 于是每过一段时间我们就要升级a...

  • Homebrew基本使用

    安装Homebrew 权限问题 Update Bug 升级: 清理: 更新: 版本查询 Homebrew 2.0....

  • Android App版本更新

    版本更新需要注意的点 兼容Android7.0 最近我们发现Android7.0的手机更新App的时候会出andr...

  • iOS 蓝牙固件升级

    升级介绍 蓝牙固件升级是使用手机给固件进行版本升级,以达到修复bug或者添加新功能的作用。升级的大概流程是:首先,...

  • 2018-04-05

    《版本》2.0《版本》《更新内容》更新接口 更新功能 修复BUG《更新内容》《更新地址》《更新地址》《软件大小》2...

  • 更新

    更新内容修复Bug更新内容增加了功能更新内容 下载链接http://pan.baidu.com/share/lin...

  • 班费统计系统软件更新

    【更新内容】修复BUG,更新功能【更新内容】【版本】1.0【版本】【大小】2M【大小】【地址】www.xdwl52...

网友评论

    本文标题:Android7.0更新升级功能bug分析

    本文链接:https://www.haomeiwen.com/subject/yjqieftx.html