FileUriExposedException
出现原因: 对于面向 Android N 的应用,Android 框架执行的 StrictMode,API 禁止向您的应用外公开 file://URI。
解决方法 FileProvider
注册 Manifest
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider" //provider的类名
android:authorities="${applicationId}.provider"
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 为android.support.v4.content.FileProvider,这是固定的,不需要手动更改;
设置 android:authorities 为 application id + .provider ;
设置 android:exported 为 false ,表示 FileProvider 不是公开的;
设置 android:grantUriPermissions 为 true 表示允许临时读写文件。
${applicationId} 是占位符,Gradle 会替换成我们在 build.gralde 中定义的 applicationId "com.domain.example",如果 build.gradle 文件中没有定义,那么 application id的默认值是 App 的 package name。
file_paths 指定的有效文件
file_paths: res 目录下新建一个 xml 文件夹,然后创建file_paths xml 文件,内容
<?xml version="1.0" encoding="utf-8"?>
<!--适配7.0 权限 临时访问文件的权限-->
<paths>
<external-path
name="external_files"
path="."/>
</paths>
含义:
其中 name 属性和 path 属性必填, name 表示共享文件的名字, path 代表文件路径。
external-path 代表文件位于手机外部存储空间,访问效果如同 Environment.getExternalStorageDirectory();
files-path 代表文件位于手机内部存储空间,访问效果如同 getFilesDir();
cache-path 代表文件位于手机内部缓存空间,访问效果如同 getCacheDir()。
使用:以打开PDF文件为例
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
try {
//判断是否是AndroidN以及更高的版本
//设置intent的data和Type属性。android 7.0以上crash,改用provider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri fileUri = FileProvider.getUriForFile(this,
getPackageName() + ".provider", file);//android 7.0以上
intent.setDataAndType(fileUri, "application/pdf");
grantUriPermission(MyWebViewActivity.this, fileUri, intent); //添加权限
} else {
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/pdf");
}
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(VActivity.this, "手机暂无pdf阅读器,请下载后重试", Toast.LENGTH_SHORT).show();
}
//添加权限
private void grantUriPermission(Context context, Uri fileUri, Intent intent) {
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}