WOODONCHAN

Android 7.0及其以上拍照出错 - FileUriExposedException

前一阵子把手机升级到了Android7.0版本,装入了公司的APP进行把玩,顺便看一看有没有BUG出现,当进行拍照时候出错了程序崩溃了,当时一头雾水的看了错误日志,搜索了相关资料终于知道原因了,接下来我们先来说一说导致这个错误的原因。

随着Android7.0的到来,Google为了进一步提高私有文件的安全性,Android收紧了私有文件的访问权限,例如先前我们拍照都是传递 file:/// Uri链接到相机进行拍照的,现在如果在Android7.0及其以上版本继续使用这种方式就会导致FileUriExposedException错误,官方文档对该问题的解释为,出于安全考虑在Android7.0以及更高版本上不支持
file:/// Uri这种链接,禁止向自己本身以外的应用公开 file:/// Uri链接,继续使用会引发FileUriExposedException 错误。因此我们现在需要使用FileProvider转换Uri才行。

上面提到的FileProvider是什么鬼+_+,接下来就先给大家介绍下FileProvider

FileProvider

FileProvider是一个特殊的ContentProvider子类,本质上还是基于ContentProvider的实现,FileProvider可以把 file:/// Uri 转换为一个安全的共享链接 content:// Uri,使用 content:// 这样可以更便利更安全为其他APP共享文件,先前使用的 file:/// Uri 从根本上来讲是并不安全的。

更多关于FileProvider的使用方法请阅读以下链接
https://developer.android.com/reference/android/support/v4/content/FileProvider.html

兄弟,莫着急,实际使用中还需要配置相关代码,具体如何使用请继续看下文 O(∩_∩)O

解决 FileUriExposedException

首先在AndroidManifest.xml进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
...
</application>

配置FileProvider文件共享路径
在res中创建xml文件夹中创建files-path.xml文件

1
2
3
4
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>

name=”my_images”:代表文件路径files-path的名称,告诉FileProvider通过my_images去访问files/images/路径的目录
path=”images/“:所需要共享文件的地址

paths可以添加多个子路径

1
2
3
4
5
<files-path name="name" path="path" /> //等于 Context.getFilesDir().
<cache-path name="name" path="path" /> //等于 Context.getCacheDir().
<external-path name="name" path="path" /> //等于 Environment.getExternalStorageDirectory().
<external-files-path name="name" path="path" /> //等于 Context.getExternalFilesDir().
<external-cache-path name="name" path="path" /> //等于 Context.getExternalCacheDir().

升级拍照代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private String mCurrentPhotoPath = "";
private File createImageFile() throws IOException {
File file = File.createTempFile("IMG-" + new Date().getTime(), ".jpg", getFilesDir().getAbsoluteFile());
mCurrentPhotoPath = file.getAbsolutePath();
return file;
}
private static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
...
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this, "com.example.android.fileprovider",photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}

切记需要进行版本判断,以适应不同版本的拍照。

1
2
3
4
5
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//7.0及其以后版本使用升级后的代码处理
} else {
//7.0之前还保持原来方案进行处理即可
}

到这里代码就写完了,大家快去升级自己的拍照代码吧, O(∩_∩)O