FileProvider
FileProvider 这个组件在Android 22.0.0 (也就是 Android 5.0 ) 版本下加入进Android系统,该组件是ContentProvider的子类,功能就是用来提供文件在跨进程间的访问能力。大家千万不要觉得是Android 7.0 才加入进来的。
为什么Android 7.0 文件共享要使用FileProvider
这个原因是因为,Android 7.0 版本以后,对于StrictMode的审查又严格了一步,就如在Android 3.0以后加入的NetWorkOnMainThread的异常一样,Google对于会对Android系统造成体验或者安全问题的部分会使用严格模式来限制程序开发者。
对于文件访问在Android 7.0以前可以使用file://uri的方式访问,但是这个地方有个问题,就是即使不是你自身应用产生的文件,只要知道对方的uri则就可以调用到,这样在安全性上就产生了风险。所以Android 7.0后新增了对文件跨进程访问的限制,这个限制会造成,如果使用file://uri的方式访问,则会出现android.os.FileUriExposedException的异常。
FileProvider 使用
Step 1 AndroidManifest ,注册FileProvider
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
...>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.unreal.update"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/update_files" />
</provider>
...
</application>
</manifest>
需要注意的地方是
android:authorities 参数,如同ContentProvider,此处需要你提供一个uri的authorities,以便于content://android:authorities/uri访问到,android:authorities可以随意定义
Step 2 res下创建xml文件夹,并在xml文件夹下创建update_files.xml
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<files-path path="files" name="files" />
<cache-path path="files" name="cache" />
<external-path path="files" name="external" />
<external-files-path path="files" name="externalfiles"/>
<!-- 此标签需要 support 25.0.0以上才可以使用-->
<external-cache-path path="files" name="externalcache"/>
</paths>
</resources>
name:名称标志字符串,不可以同名!
path:文件夹“相对路径”,完整路径取决于当前的标签类型。
标签 | 路径 |
---|---|
…….. | * 代表 当前文件夹及其子文件夹 |
file-path | 物理路径为Context.getFilesDir() + /files/* |
cache-path | 物理路径为Context.getCacheDir() + /files/* |
external-path | 物理路径为Environment.getExternalStorageDirectory() + /files/* |
external-files-path | 物理路径为Context.getExternalFilesDir(String) + /files/* |
external-cache-path | 物理路径为Context.getExternalCacheDir() + /files/* |
在简书看到 zhuhf 大神的关于FileProvider的文章后,发现还有一个隐藏的标签
<root-path name="name" path="path" />
标签 | 路径 |
---|---|
…….. | * 代表 当前文件夹及其子文件夹 |
root-path | 物理路径相当于 /path/* |
Step 3 通过getUriForFile获取到uri
data = FileProvider.getUriForFile(context,"之前在AndroidManifest中配置的android:authorities", "7.0后文件的路径");
// 给目标应用一个临时授权
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
FLAG_GRANT_READ_URI_PERMISSION:表示读取权限;
FLAG_GRANT_WRITE_URI_PERMISSION:表示写入权限;
根据你的需求,是读取呢,还是写入自行选择
例子 安装APK
private void installApk(File apk) {
if (!apk.exists()) {
return;
}
Uri data;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// UpdateConfig.FILE_PROVIDER_AUTH 即是在清单文件中配置的authorities
data = FileProvider.getUriForFile(context, UpdateConfig.FILE_PROVIDER_AUTH, apk);
// 给目标应用一个临时授权
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
data = Uri.fromFile(apk);
}
intent.setDataAndType(data, "application/vnd.android.package-archive");
context.startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
}