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

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分钟整理,但是积累多了也是一份宝贵的财富。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,384评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,345评论 0 17
  • 今天早上阳光明媚,带给我舒畅的心情。由于这两天能量有些不稳定,一是因为对女儿熬夜焦虑加重,二是对她熬夜的学习效率产...
    菲儿_247c阅读 155评论 3 4
  • 我在等一个人……一个愿意走进我的生命分享我的喜怒哀乐, 同时也愿意让我走进他的生命体会他的爱恨情仇的人。 一个不是...
    也许我错了阅读 279评论 0 0
  • 昨日做下的事: 挤地铁去参加培训,进店体验,开始工作。 整理收拾工作宿舍,并住下来。 打靶练习散打泰拳,玩仿真人靶...
    文建伟CZYH阅读 231评论 3 1