Android 7.0 + FileProvider 访问隐私文
2019-07-18 本文已影响0人
12313凯皇
一、概述
从 Android 7.0
开始,Android SDK 中的 StrictMode 策略禁止开发人员在应用外部公开 file:// URI
。具体表现为,当我们在应用中使用包含 file:// URI
的Intent
离开自己的应用时,程序会发生FileUriExposedException
异常
这里我们要使用到的 FileProvider
,就是 ContentProvider
的一个特殊子类,帮助我们将访问受限的 file:// URI
转化为可以授权共享的 content:// URI
。
简单来说就是在Android 7.0
及以上机型中如果需要访问手机文件,那么必须使用 FileProvider
来进行适配,否则程序会报错。
二、使用
- 在
AndroidManifest.xml
中注册FileProvider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/files_paths" />
</provider>
注
- 这里的
authorities
可以自己定义,不过后面需要用到,只要保证前后一致即可。 -
resource
内的xml
文件是用来配置文件路径的,文件名可自定义。
- 在
src\main\res\xml\
文件夹下创建对应的files_paths.xml
文件
<!--files_paths.xml-->
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--此处的name可以随便取-->
<external-path
name="external_storage_root"
path="."/>
<external-files-path name="document" path="Documents"/>
<external-files-path name="download" path="Download" />
<external-files-path name="pictures" path="Pictures" />
</paths>
xml
中元素必须包含一到多个子元素。这些子元素用于指定共享文件的目录路径,必须是这些元素之一:
-
<files-path>
:内部存储空间应用私有目录下的files/
目录,等同于Context.getFilesDir()
所获取的目录路径; -
<cache-path>
:内部存储空间应用私有目录下的cache/
目录,等同于Context.getCacheDir()
所获取的目录路径; -
<external-path>
:外部存储空间根目录,等同于Environment.getExternalStorageDirectory()
所获取的目录路径; -
<external-files-path>
:外部存储空间应用私有目录下的files/
目录,等同于Context.getExternalFilesDir(null)
所获取的目录路径; -
<external-cache-path>
:外部存储空间应用私有目录下的cache/
目录,等同于Context.getExternalCacheDir()
;
- 使用
FileProvider
进行转换:
//注意此处的 第二个参数必须与 Manifest文件中Provider 的 android:authorities 一致
Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".FileProvider", outputFile);
此处需要传进三个参数,第一参数是context
上下文,第二个参数是FileProvider
,必须与 AndroidManifest.xml
文件中Provider的 android:authorities
一致,第三个参数是file
文件对象。
下面再放上一个例子:
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
String type = MimeTypesTools.getMimeType(context, path);
File file = new File(path);
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
uri = FileProvider.getUriForFile(mContext,
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);BuildConfig.APPLICATION_ID+".fileprovider", file);
}else {
uri = Uri.fromFile(file);
}
intent.setDataAndType(uri, type);
context.startActivity(intent);
划重点!!
一定要给Intent
加一个flag
——Intent.FLAG_GRANT_READ_URI_PERMISSION
,否则会报Permission Denial
的错,当然7.0
以下的机型是不需要的。