7.0新特性-私有目录被限制访问
卓官方为了提高私有文件的安全性,面向 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); }
鸿洋的总结,工具类:http://blog.csdn.net/lmj623565791/article/details/72859156
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 { }