Android 之 FileProvider

实现一个拍照上传的功能,在miui机型上会抛出FileUriExposedException异常。检查后发现是Android API有变化。从Android 7.0开始,不再允许在app中把file://Uri暴露给其他app,否则应用会抛出FileUriExposedException。原因在于,Google认为使用file://Uri存在一定的风险。比如,文件是私有的,其他app无法访问该文件,或者其他app没有申请READ_EXTERNAL_STORAGE运行时权限。解决方案是,使用FileProvider生成content://Uri来替代file://Uri。

FileProvider官方文档:https://developer.android.com/reference/android/support/v4/content/FileProvider.html

在清单文件上声明FileProvider(注意在application标签里声明才有用)。

android:name是固定写法。
android:authorities可自定义,是用来标识该provider的唯一标识,建议结合包名来保证authority的唯一性。
android:exported必须设置成 false,否则运行时会报错java.lang.SecurityException: Provider must not be exported 。
android:grantUriPermissions用来控制共享文件的访问权限。

<meta-data>节点中的android:resource指定了共享文件的路径。此处的provider_paths即是该Provider对外提供文件的目录的配置文件,存放在res/xml/下。
provider_paths.xml内容如下:

其中根元素<paths>是固定的,内部元素可以是以下节点:
<files-path name=”name” path=”path” /> 对应getFilesDir()。
<cache-path name=”name” path=”path” /> 对应getCacheDir()。
<external-path name=”name” path=”path” /> 对应Environment.getExternalStorageDirectory()。
<external-files-path name=”name” path=”path” /> 对应getExternalFilesDir()。
<external-cache-path name=”name” path=”path” /> 对应getExternalCacheDir()。

这里注意,如果不添加root-path标签,使用FileProvider构造SD卡中文件uri时会异常。
<root-path name=”root_path” path=”.” />

为了向下兼容,在v4包中提供了FileProvider类:android.support.v4.content.FileProvider

可以看到之前使用
Uri uri = Uri.fromFile(captureFile);
获取文件保存的路径,现在使用FileProvider获取了。

重新编译程序,在android 7.0上完美运行。之前app升级功能,在android 7.0上异常也是这个原因造成的。文件下载后不能直接访问,要通过FileProvider。

android系统发展太快了,虽然一直向更好的方向发展,但对于开发者来说,需要时常关注api的变更。