最近又碰到因为android 高版本引起的兼容问题:
在7.0 之前 获取文件Uri 是通过 Uri uri = Uri.fromFile(file);
今天测试拿到一台8.0的android机测试分享删退
通过日志发现 android.os.FileUriExposedException ,属于系统兼容问题
科普:
Android不再允许在app中把file://Uri暴露给其他app,包括但不局限于通过Intent或ClipData 等方法
原因在于使用file://Uri会有一些风险,比如:
文件是私有的,接收file://Uri的app无法访问该文件。
在Android6.0之后引入运行时权限,如果接收file://Uri的app没有申请READ_EXTERNAL_STORAGE权限,在读取文件时会引发崩溃。
因此,google提供了FileProvider,使用它可以生成content://Uri来替代file://Uri。
解决方案 参考 https://www.jianshu.com/p/55b817530fa3
首先在AndroidManifest.xml中添加provider
android:authorities
是用来标识provider的唯一标识,在同一部手机上一个"authority"串只能被一个app使用,冲突的话会导致app无法安装。
android:exported必须设置成false,后面异常会讲为什么
android:grantUriPermissions用来控制共享文件的访问权限,也可以在java代码中设置。
<manifest xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
package=
"com.xxx.xxxx"
···
<provider
android:name=
"android.support.v4.content.FileProvider"
android:authorities=
"com.xxx.xxxx.fileprovider"
android:exported=
"false"
android:grantUriPermissions=
"true">
<meta-data
android:name=
"android.support.FILE_PROVIDER_PATHS"
android:resource=
"@xml/provider_paths" />
</provider>
</manifest >
res/xml/provider_paths.xml
这是指定路径和转换规则
<paths>
中可以定义以下子节点
子节点 对应路径 例子 files-path Context.getFilesDir() cache-path Context.getCacheDir() external-path Environment.getExternalStorageDirectory() /storage/emulated/0/ external-files-path Context.getExternalFilesDir(null) external-cache-path Context.getExternalCacheDir()
加入我要替换的目录是
/storage/emulated/0/diary sdcard/photo/
那么配置应该写成
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="diary sdcard/photo"/>
</paths>
然后修改代码
//创建临时图片
File photoOutputFile = SDPath.getFile("temp.jpg", SDPath.PHOTO_FILE_STR);
//Uri photoOutputUri = Uri.fromFile(photoOutputFile);
Uri photoOutputUri = FileProvider.getUriForFile(
mContext,
mActivity.getPackageName() + ".fileprovider",
photoOutputFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);