卓官方为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问 (0700)。此设置可防止私有文件的元数据泄漏,如它们的大小或存在性.
传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider。
1.定义一个FileProvider(由于FileProvider是继承ContentProvider,属于四大组件之一,需要在AndroidManifest.xml中配置)
注意一点,android:authorities
不要写死,因为该library最终可能会让多个项目引用,而android:authorities
是不可以重复的,如果两个app中定义了相同的,则后者无法安装到手机中(authority conflict)。
<!-- fileProvider 用于兼容7.0 添加可用权限的文件目录-->
<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/file_provider_paths"/>
</provider>
2.添加可用权限的文件目录
在res资源目录下新建xml文件下,在该文件夹下创建file_provider_paths.xml文件,这个xml文件名并不是一定要这么起,只要和清单文件中配置的文件名一致就行
<?xml version="1.0" encoding="utf-8"?>
<!--新建xml文件夹, src\main\res\xml\file_provider_paths.xml-->
<resources>
<paths>
<!-- path="",是有特殊意义的,它代码根目录(下面同理),也就是说你可以向其它的应用共享根目录
及其子目录下任何一个文件了,如果你将path设为path=”pictures”,
那么它代表着根目录下的pictures目录(eg:/storage/emulated/0/pictures)
即:Environment.getExternalStorageDirectory()目录或者其子目录。-->
<external-path path="" name="myFile"></external-path>
<!--表示Context.getFilesDir()目录或者其子目录。
示例 : /data/data/com.chen.gradle/files/test1-->
<files-path name="name1" path="test1" />
<!--表示Context.getCacheDir()目录或者其子目录。
示例 : /data/data/com.chen.gradle/cache/test2-->
<cache-path name="name2" path="test2" />
<!--表示Context.getExternalFilesDir(null)目录或者其子目录。
示例 : /storage/emulated/0/Android/data/com.chen.gradle/files/test4-->
<external-files-path name="name4" path="test4" />
<!--表示Context.getExternalCacheDir()目录或者其子目录。
示例 : /storage/emulated/0/Android/data/com.chen.gradle/cache/test5-->
<external-cache-path name="name5" path="test5" />
</paths>
</resources>
3.增加到provider
4.通过provider生成Uri
5.赋予临时权限给Uri
比如安装apk时:
public static void installApk(Context context, String apkPath) {
if (context == null || TextUtils.isEmpty(apkPath)) {
return;
}
File file = new File(apkPath);//将该路径增加到provider
Intent intent = new Intent(Intent.ACTION_VIEW);
//判读版本是否在7.0以上
if (Build.VERSION.SDK_INT >= 24) {
//通过provider生成Uri 需要与在清单文件中注册的provider的 authorities一致
Uri apkUri = FileProvider.getUriForFile(context, "工程包名.fileprovider", file);
//赋予临时权限给Uri
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
context.startActivity(intent);
}
鸿洋的总结,工具类:
public class FileProvider7 {
public static Uri getUriForFile(Context context, File file) {
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= 24) {
fileUri = getUriForFile24(context, file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
private static Uri getUriForFile24(Context context, File file) {
Uri fileUri = android.support.v4.content.FileProvider.getUriForFile(context,
context.getPackageName() + ".myfileprovider",//切记需要与清单文件中的authorities一致
file);
return fileUri;
}
/**
*
* @param context
* @param intent
* @param type
* @param file
* @param writeAble 是否赋予写入权限,比如安装apk的时候就需要
*/
public static void setIntentDataAndType(Context context,
Intent intent,
String type,
File file,
boolean writeAble) {
if (Build.VERSION.SDK_INT >= 24) {
intent.setDataAndType(getUriForFile(context, file), type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(Uri.fromFile(file), type);
}
}
}
切记:
但是此时我们项目中可能会用到其他一些第三方sdk有用到拍照功能的话,他也为了适配android7.0也添加了这个节点,其实很简单我们只要重写一个类 继承自FileProvider,然后就按上述方法在添加一个节点就可以了;例如;
<!--但是此时我们项目中可能会用到其他一些第三方sdk也配置了FileProvider节点,
此时有些人可能就不知道如何下手了,其实很简单我们只要重写一个类 继承FileProvider,
然后就按上述方法再添加一个节点就可以了;例如;-->
<provider
android:name=".service.UpdateFileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/app_file_paths"/>
</provider>
所以:切记android:authorities="${applicationId}.fileProvider"
import android.support.v4.content.FileProvider;
/**
* 解决需要添加多个FileProvider的问题
*/
public class UpdateFileProvider extends FileProvider {
}