最近因为上级要求,需要做个人信息安全整改。这样一改,涉及到的不单单是以前的旧技术,还有安全级别越来越高的手机系统。对于开发层面来说,因为项目庞大、涉及业务比较多,千丝万缕,为了防止一个bug倒下,千千万万个bug站起来的问题,全盘更改代码,是不可能的。所以,找出新手机系统特征,是更改兼容的突破口,正所谓知己知彼,百战不殆。
当时主要是mate30出现这个问题。在编写代码和测试过程中,遇到的问题是:FileUriExposedException(这里我已经在清单文件AndroidManifest.java增加了 provider)。
具体错误信息:
2020-03-11 11:01:34.572 32359-32359/com.gdtel.eshore.check E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.*.*.*, PID: 32359
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/[自定义文件夹]/[名称].apk
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:678)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:377)
atcom.*.*.*.activity.verupdate.VerUpdateActivity$DownloadTask.onPostExecute(VerUpdateActivity.java:258)
atcom.*.*.*.activity.verupdate.VerUpdateActivity$DownloadTask.onPostExecute(VerUpdateActivity.java:188)
at android.os.AsyncTask.finish(AsyncTask.java:727)
at android.os.AsyncTask.-wrap1(Unknown Source:0)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:744)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7529)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
先来了解一下,Android各个版本都更改了些什么,这里讲到新功能,主要是我们开发的时候,需要注意兼容的地方。
android10.0参考资料(更多资料…搜索吧)
Android Q Beta开发者文档链接::https://developer.android.com/preview
Android Q Beta镜像下载链接:https://developer.android.com/preview/download
Android Q Beta 发布 blog:https://android-developers.googleblog.com/2019/03/introducing-android-q-beta.html
非SDK接口:https://juejin.im/post/5afe50eef265da0b70262463
以往我们这样简单的写就能实现应用内自动更新。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
finish();
startActivity(intent);
其中,file参数是一个通过APK文件的地址获取的File对象,比如你的APK下载地址是/storage/emulated/0/xxx/[名称].apk,则传入new File(“/storage/emulated/0/xxx/[名称].apk”)。简单粗暴,效果显著。到了Android 7.0之后,继续沿用这部分代码的时候,就会发现问题了
android.os.FileUriExposedException: file:///storage/emulated/0/1.apk exposed beyond app through Intent.getData()
当你的应用把file:// Uri暴露给其他App的时候就会出现这种异常,因为接收方Ap可能并不具备访问该共享资源的权限。所以应该用content:// Url来拓展临时权限,这样接收方就能访问到资源了。显然,这是Google为了收紧Android的自由度,提升安全度所做的事情
对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。
要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类,关于FileProvider使用可以查看
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="当前包名.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"
/>
</provider>
AndroidX第一行应变为 -------->这里不懂为什么???
androidx.core.content.FileProvider
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external" path=""/>
</paths>
Intent intent = new Intent(Intent.ACTION_VIEW);
File apkFile = new File(apkSavePath);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".fileprovider", apkFile);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
}
startActivity(intent);
其中apkSavePath,我的程序放到了sd卡下
//获取SD卡的根路径
String sdcardRoot = Environment.getExternalStorageDirectory().getAbsolutePath();
final String apkSavePath = sdcardRoot+"/1.apk";
对于 filepaths.xml文件里path的填写,需要看看当前代码下载的apk保存在哪里,如果保存在外存储的,则需要用 ,如果内存的放在 ,更多的关于它的参数,请查看:https://blog.csdn.net/u010356768/article/details/89214265
对这些代码做些解释,如果系统低于Android 7.0,则还是使用老一套,否则就要走FileProvider路线。
给intent设置了FLAG_GRANT_READ_URI_PERMISSION后,就代表在启动第三方Activity(实际上就是你要把自己的文件共享给的第三方App)的时候,授予对方临时读取URI所映射的目录的权限:
"Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + “.fileprovider”, apk_file);"则通过FileProvider将我们的文件(这里就是新版本的APK文件)的路径转化为了content://Uri,这样就实现了可保证安全的临时文件共享功能,如此一来系统负责处理该intent的应用就能顺利安装APK了。
对于兼容性的问题,还是得看官方最新文档。
奈何我的总结能力还是小学水平,很多摘抄,在此特别感谢这些默默哥(因为没和他们有过任何互动,所以叫默默哥):Errol_King、安卓搬砖小曾。
篇外话,这个bug更改了几天,但是依然就觉得,如果不用自己的话写出来,留下点什么证据,印象不深。虽然大多证据都是偷的,但是依然相信自己有天写的东西,可以给别人偷,哈哈哈~~