FileProvider是一个特殊的ContentProvider子类,它将 content:// Uri 代替了 file:/// Uri ,从而促进与应用程序关联的文件的安全共享.
- 在Android N 7.0上,没有使用FileProvider访问其他文件可能会出现如下异常:
AndroidRuntime: FATAL EXCEPTION: Thread-2
AndroidRuntime: Process: com.android.systemui:screenshot, PID: 6131
AndroidRuntime: android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/Screenshots/19700102_085501.png exposed beyond app through Intent.getData()
解决上述问题,我们需要定义一个FileProvider.
- 1.在AndroidManifest中添加FileProvider
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
</application>
</manifest>
android:name: 指向v4包里的FileProvider类
android:authorities: content uri的基础域名,生成的uri将以content://com.mydomain.fileprovider为前缀
android:exported: 设置为 false, FileProvider 不需要被其他程序访问(为true则会报安全异常)
android:grantUriPermissions: 设为 true, 授予 URI 临时访问权限
resource: 中的@xml/file_paths是我们接下来要添加的文件
- 2.添加允许FileProvider访问的路径
也就是上面meta-data 中的resource定义的xml(该文件添加在res/xml 目录中)
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>
name="my_images" 需要临时授权访问路径名称
path="images/" 临时授权访问的路径(.代码所有路径)
根元素<paths>是固定的,内部元素可以是以下节点:
<files-path name="name" path="path" /> 对应getFilesDir() "/data/data/org.code/files"
<cache-path name="name" path="path" /> 对应getCacheDir() "/data/data/org.code/cache"
<external-path name="name" path="path" /> 对应Environment.getExternalStorageDirectory() "/storage/emulated/0"
<external-files-path name="name" path="path" /> 对应getExternalFilesDir() "/storage/emulated/0/Android/data/org.code/files"
<external-cache-path name="name" path="path" /> 对应getExternalCacheDir() "/storage/emulated/0/Android/data/org.code/cache"
- 3.修改调用代码
Uri data;
//判断是否是AndroidN以及更高的版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// com.mydomain.fileprovider是在清单文件中配置authorities
data = FileProvider.getUriForFile(context, "com.mydomain.fileprovider", file);
} else {
data = Uri.fromFile(file);
}
- 可能会出现如下错误:
造成该问题原因是因为配置的path的路径有误.
AndroidRuntime: FATAL EXCEPTION: Thread-2
AndroidRuntime: Process: com.android.systemui:screenshot, PID: 6601
AndroidRuntime: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Pictures/Screenshots/19700102_090838.png
Android官方文档中没有找到FileProvider对外置SD卡的path设置,查看FileProvider源码.我们可以如下方式设置来对外置SD卡的支持
root-path代表/ 也就是Android设备的根目录,该目录下包含着手机内部存储器,外置SD卡等所有文件的目录.
<paths xmlns:android="http://schemas.android.com/apk/res/android>
<root-path name="root" path="" />
</paths>
- 可能会出现如下错误:
AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.Provider
Info.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
ent.pm.PackageManager, java.lang.String)' on a null object reference
AndroidRuntime: at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:583)
AndroidRuntime: at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:557)
AndroidRuntime: at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:399)
AndroidRuntime: at com.android.systemui.screenshot.SuperShotUtils.getImageFileIntent(SuperShotUtils.java:66)
AndroidManifest中authorities和代码中authority对应不上.
解决方案: 两个authority保持一致(还有一种情况是两者一致,但是依然报错.可以卸载apk再试,或者去掉AndroidManifest中该provider编译安装一个apk,然后加上再编译安装一个apk).