问题描述
我们在进行Android开发的时候,一般都会在应用里检测有没有更新,并且从网上下载最新的版本包,覆盖本地的旧版本。在我的项目中,出现了一个问题,就是当安装包下载到本地的时候,产生了“解析包时出现问题”这样的错误,导致无法安装相应的新版本。同时,值得一提的是,这个问题只有在我们用代码调用去进行安装的时候才会发生,我们手动去用安装包覆盖本地的旧包则没有出现问题。经过反复的测试,发现这个问题只会发生在Android 7.0版本的设备上,在较低版本的设备上则无这个问题。
这也是在网上比较通用的方案,就是在打包的时候我们使用了V2签名导致无法进行安装。
Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 会使用 APK Signature Scheme v2 和传统签名方案来签署应用。
这项新方案并非强制性的,如果应用在使用 APK Signature Scheme v2 时不能正确开发,可以停用这项新方案。禁用过程会导致 Android Studio 2.2 和 Android Plugin for Gradle 2.2 仅使用传统签名方案来签署应用。要仅用传统方案签署,打开模块级 build.gradle 文件,然后将行 v2SigningEnabled false 添加到版本签名配置中:
android { … defaultConfig { … } signingConfigs { release { storeFile file(“myreleasekey.keystore”) storePassword “password” keyAlias “MyReleaseKey” keyPassword “password” v2SigningEnabled false } } }
根据官方文档,就是在我们的gradle文件里的相应位置添加这行代码
v2SigningEnabled false
解决方案2
但是,在我的情境中,即使使用了方案一,仍然在更新包下载完成后显示了“解析包时出现问题”这样的字样,导致下载更新流程出错。
这个时候,就有可能是另外一种情况:当我们在7.0上使用系统服务去下载apk并且试图安装时,有可能由于权限问题导致安装失败。
每个Android版本的发布,对于安全性问题的要求越来越高,也为Android程序员增加了额外的工作量。Android6.0引入动态权限控制(Runtime Permissions),Android7.0引入私有目录被限制访问和StrictMode API 。私有目录被限制访问是指在Android7.0中为了提高应用的安全性,在7.0上应用私有目录将被限制访问,这与iOS的沙盒机制类似。StrictMode API是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,则会报出异常。
以下是我原来在7.0上出问题的代码:
Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive"); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install);
从代码中可以看出,Uri.fromFile导致我们在7.0上出现了问题,它其实就是生成一个file://URL。这就是为什么在下载完成后,调用这段代码去安装的时候出错,因为一旦我们通过这种办法打开系统安装器,就认为file:// URI类型的 Intent 离开我的应用,这样程序就会发生异常;而我们手动去把安装包覆盖原来的旧包则没有问题。
解决方案将使用FileProvider,它的步骤是:
第一步:
在AndroidManifest.xml中注册provider,provider可以向应用外提供数据。
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.dafangya.app.pro.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
其中com.dafangya.app.pro是我的应用包名。
第二步:
- 在res/xml/file_paths.xml创建文件。 内容为:
-
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path path="" name="download"/> </paths> </resources>
- 第三步:
-
String fileName = cursor.getString(fileNameIdx);//承接我的代码,filename指获取到了我的文件相应路径 if (fileName != null) { if (fileName.endsWith(".apk")) { if(Build.VERSION.SDK_INT>=24) {//判读版本是否在7.0以上 File file= new File(fileName); Uri apkUri = FileProvider.getUriForFile(context, "com.dafangya.app.pro.fileprovider", file);//在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);//添加这一句表示对目标应用临时授权该Uri所代表的文件 install.setDataAndType(apkUri, "application/vnd.android.package-archive"); context.startActivity(install); } else{ Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive"); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); } } }
因为对于7.0以下的设备,还是走以前的逻辑,所以上面的代码进行分情况讨论,6.0及其以下的设备还是走以前的逻辑。
- 第四步
按照大部分教程来说,第四步其实是不存在的,但是在我的项目中,运行到第三步中的代码时,还是报错了,错误如下: -
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=com.dafangya.app.pro (has extras) } in com.b.b.a.a.e$1@457cfd2 at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1229) at android.os.Handler.handleCallback(Handler.java:755) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:156) at android.app.ActivityThread.main(ActivityThread.java:6524) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:941) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:831) Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.android.providers.downloads/cache/product_other_V1.4.8ceshi.apk at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:678) at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:377) at com.example.xh.toolsdk.umeng.Downloads$1.onReceive(Downloads.java:95) at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1219) ... 7 more
- 第四步