Android 7.0 通过FileProvider共享文件
一.概述
Android 7.0后,提供了很多新特性,其中最主要的是禁止了通过file://URI直接在文件操作共享文件(该操作会触发FileUriExposedException),而是通过content://URI来实现共享。
FileRrovider是ContentProvider的子类,用于不同应用间的文件共享。
二 使用
1.在Mainfest文件中声明provider。
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider>
2.编写资源文件provider_paths。
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <root-path name = "root" path = ""/> <files-path name="files" path=""/> <cache-path name="cache" path=""/> <external-path name="external" path=""/> <external-files-path name="external-file" path=""/> <external-cache-path name="external-cache" path=""/> </paths>
在paths节点内部支持以下几个子节点,分别为:
- <root-path/> 代表设备的根目录new File("/");
- <files-path/> 代表context.getFilesDir()
- <cache-path/> 代表context.getCacheDir()
- <external-path/> 代表Environment.getExternalStorageDirectory()
- <external-files-path>代表context.getExternalFilesDirs()
- <external-cache-path>代表getExternalCacheDirs()
path代表目录下的子目录,如:
<cache-path name = "cache" path = "path />
代表context.getChcheDir()/path目录,如果path为空,代表直接使用该根目录。
既然要使用content://URI替代file://URI,那么我们需要一个虚拟路径对真实文件路径进行映射。通过编写xml文件,其中path路径确定了可访问的文件目录,name属性映射了真实文件路径。
3.使用fileProvider API 安装APK
我们一般编写安装APK操作时,是这么写的。
private void installAPK() { File file = new File(Environment.getExternalStorageDirectory(),"test.apk"); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive"); startActivity(intent); }
拿着在7.0上的手机跑一下,果不其然会报android.os.FileUriExposedException。
简单修改下URI的获取方式。
private void installAPK() { File file = new File(Environment.getExternalStorageDirectory(),"test.apk"); Intent intent = new Intent(Intent.ACTION_VIEW); Uri fileUri = null; if(Build.VERSION.SDK_INT >= 24){ //输出 content://com.example.fanggao.fgtextdemo/external/test.apk fileUri = FileProvider.getUriForFile(this,BuildConfig.APPLICATION_ID+".fileProvider",file); }else { fileUri = Uri.fromFile(file); } intent.setDataAndType(fileUri, "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivity(intent); }
这样就可以正常运行了。注意,我们还需要加上权限,否则会报Permission Denial异常。
那么问题来了,当我们直接使用fileProvider时,在5.0以下的手机也会报Permission Denial异常,是不是也可以使用addFlags()方法添加权限呢?
答案是不行的,但addFlags只是使用与setData,setDataandType以及setClipData,而且该方法在5.0以下是无效的,需要使用grantUrlPermission()方法,获取所有符合授权的应用,全部授权。
三 小结
7.0后使用fileProvider来实现文件共享,主要目的是隐藏真实的文件目录,因为fileProvider是ContentProvider的子类,所以需要在AndroidManifest.xml文件中注册;并且需要编写xml文件描述可使用的文件夹目录,通过name 去映射文件真实目录,实现访问的安全性。
另外获取授权的方式有2种,
1.通过addFlags()来获取,仅使用setData,setDataandType以及setClipData方法传递uri时支持。
2.通过grantUrlPermission()方式,具体参考下文博客。
更多参考博客:
https://blog.csdn.net/lmj623565791/article/details/72859156