一、前言
Android 7.0有很多的变化,和我们开发者关联最大的,或者说必须要适配的就是去除项目中传递 file://
类似格式的 Uri 了。
要应用间共享文件,您应发送一项 content:// URI
,并授予 URI
临时访问权限。进行此授权的最简单方式是使用 FileProvider 类,
FileProvider 实际上是 ContentProvider 的一个子类,它的作用也比较明显,file://Uri
不给用,那么换个 Uri 为 content://
来替代。
二、使用步骤
1、在AndroidManifest.xml中注册
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.developerhaoz.androidtrainingdemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"
/>
</provider>
...
</application>
</manifest>
FileProvider 是 ContentProvider 的子类,所以要声明
2、指定文件的分享路径
FileProvider 只能为指定的目录中的文件生成内容 URI。要指定目录,就必须使用 <paths>
元素的子元素在 XML 中指定其存储区域和路径,我们先创建一个名为 res/xml/file_paths.xml
的新文件:
在 file_paths.xml
文件中,便可以指定文件存储的区域和路径。例如,以下路径元素告诉 FileProvider,你打算为私有文件区域的 images/ 子目录
请求内容 URI:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="myimages" path="images/"/>
...
</paths>
<paths>
必须包含以下元素中一个或者多个子元素:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="" />
<files-path name="files" path="" />
<cache-path name="cache" path="" />
<external-path name="external" path="" />
<external-files-path name="name" path="path" />
<external-cache-path name="name" path="path" />
</paths>
2019-04-25 13:59:48.032 13704-13704/? D/dongjiejie: getFileDir----/data/user/0/packagename/files
2019-04-25 13:59:48.033 13704-13704/? D/dongjiejie: getCacheDir----/data/user/0/packagename/cache
2019-04-25 13:59:48.037 13704-13704/? D/dongjiejie: getExternalStorageDirectory---/storage/emulated/0
2019-04-25 13:59:48.040 13704-13704/? D/dongjiejie: getExternalFilesDirs-----/storage/emulated/0/Android/data/packagename/files
2019-04-25 13:59:48.042 13704-13704/? D/dongjiejie: getExternalCacheDirs-----/storage/emulated/0/Android/data/packagename/cache
每个节点都有两个属性:name、path
<external-path name="external" path="pictures"/>
代表的目录即为:Environment.getExternalStorageDirectory()/pictures,
当这么声明以后,代码可以使用你所声明的当前文件夹以及其子文件夹
3、配置工作已经全部完成了,后面就需要将之前传递的 file://
替换成 FileProvoider
需要用到的 content://
,这就需要用到 FileProvider.getUriForFile()
方法了
public static Uri getUriForFile(Context context, String authority, File file) {
final PathStrategy strategy = getPathStrategy(context, authority);
return strategy.getUriForFile(file);
}
可以看到 getUriForFile()
,需要传入 一个
authority 的参数,这正是我们前面在 AndroidManifest.xml
文件中配置的 android:authorities
参数,调用这个方法会自动得到一个 file://
转换成 content://
的一个 Uri 对象,可以供我们直接使用
4、给Uri授予临时权限
例如:7.0上调用安装界面:
private void installApk(String apkName) {
FileUtils mFileUtils = new FileUtils();
Uri mUri;
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
//7.0系统开始认为直接使用本地Uri是不安全的 所以以FileProvider共享给外部提高安全性
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mUri = FileProvider.getUriForFile(this, "com.manager.carmanager.fileProvider", mFileUtils.getApksFile(apkName));
} else {
mUri = Uri.fromFile(mFileUtils.getApksFile(apkName));
}
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(mUri,
"application/vnd.android.package-archive");
startActivityForResult(intent, 10025);
}
总结:
Android 7.0 禁止在应用外部公开 file:// URI
,所以我们必须使用 content://
替代 file://
,这时主要需要 FileProvider 的支持,而因为 FileProvider 是 ContentProvider 的子类,所以需要在 AndroidManifest.xml
文件中进行注册,而又因为需要对真实的 filepath
进行映射,所以需要编写一个 xml 文档,用于描述可使用的文件夹目录,以及通过 name 去映射该文件夹目录。当我们生成一个 content://
的 Uri 对象之后,还需要对这个 Uri 接收的 App 赋予对应的权限