FilePrivider
是ContentPrivider
的特殊子类,是为了能安全地向其他App分享文件而定义的。它用content://Uri
创建一个文件替代Android7.0之前使用的的file://Uri
。
content Uri
使用临时的权限来允许读写,当你创建一个包含content Uri
的Intent
,为了把它发送给目标App(client app),你可以调用Intent.addFlags()
来添加权限,只要目标的activity
是激活状态,权限会一直存在。
对于打开Service
的Intent
,只有Service
还在运行,则权限会一直存在。
作为对比,为了获得特定file Uri
文件的权限你必须更改整个文件系统的权限,这样其他所有的app都有了读取的权限,而且权限会一直存在知道你改变它。这种的读取方式从根本上来说是不安全的。
下面记录一下在Android 7.0之后如何使用(示例是打开某种文件的场景,如打开APK安装包,打开.doc文件等)
1.menifests
中配置provider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
2.创建filepaths.xml
文件(在res文件夹下新建xml文件夹,文件名可随意)
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path
name="apk"
path="/upgrade" />
<external-cache-path
name="apk"
path="/upgrade" />
</paths>
参考 FilePrivider Specifying Available Files
<paths>
标签下可以包含以下几种子标签
<files-path name="" path="" />
其中path
对应Context.getFilesDir(),像path="/upgrade"
就是getfilesDir()
下的子文件夹upgrade
,name
可以随意取名
<cache-path name="name" path="path" />
<external-path name="name" path="path" />
对应Environment.getExternalStorageDirectory()
<external-files-path name="name" path="path" />
对应Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)
<external-cache-path name="name" path="path" />
对应Context.getExternalCacheDir()
3.向App外发送文件(这里是APK安装程序)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// SDK24以上
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", apkFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} else {
// SDK24以下
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
其中的apkFile
是文件路径名,我是将下载的apk放在缓存目录中
public static File cacheApkFile(Context context) {
File file;
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
// sdcard
file = new File(context.getExternalCacheDir(), "upgrade");
if (!file.exists()) {
file.mkdir();
}
file = new File(file, "cache.apk");
} else {
// 内存
file = new File(context.getCacheDir(), "upgrade");
if (!file.exists()) {
file.mkdir();
}
file = new File(file, "cache.apk");
}
return file;
}
因为在cache
文件夹下新建了子目录upgrade
,所以filepaths.xml
文件需要临时对外开放这个子目录的权限