Android 文件管理系列 (02) – FileProvider

之前的文章有讲过,/data/data/<package-name>/<some-external-storage/Android/data/<package-name> 等应用私有目录无法被其他应用访问,如果需要在其他应用内打开私有内容,需要使用到 FileProvider.
FileProvider 是继承自 ContentProvider 的一个子类,用于安全地将一个应用相关的文件通过 content:// Uri 而不是 file:/// Uri 分享给其他用户。
Content Uri (schema 为 content://) 允许你临时赋予读写权限。当你发送一个包含 content Uri 的 Intent 给目标 app 时,使用 Intent.setFlags(), Intent.addFlags() 方法为其赋予权限。

系列

Android 文件管理系列 (00) – 存储系统概述
Android 文件管理系列 (01) – 获取挂载点和权限
Android 文件管理系列 (02) – FileProvider [当前]
Android 文件管理系列 (03) – 监控文件变化

FileProvider 用法

声明

首先在 AndroidManifest 中声明 FileProvider:

<provider
    android:exported="false"
    android:grantUriPermissions="true"
    android:authorities="@string/authority_file_provider"
    android:name="android.support.v4.content.FileProvider">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/paths"/>
</provider>

exported: 在较新的 Android 上必须设置为 false, 否则应用启动时会直接 FC
grantUriPermissions: 如果要使文件能被其他应用访问,则必须设置为 true
authorities: 定义授权信息

指定分享的文件

所有需要通过 FileProvider 使用的目录在 android.support.FILE_PROVIDER_PATHS 这一项中设置,其中 paths 是一个定义了目录列表的 xml 文件, 例如:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <files-path path="providers" name="intfiles" />
    <external-path path="providers" name="extfiles" />
</paths>

由文件生成 Content Uri

由于 FileProvider 使用 Content Uri, 所以我们需要将 file:/// 转换为 content://. 接收的 app 可以通过 ContentResolver.openFileDescriptor 来获得 ParcelFileDescriptor.

File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "some_image.jpg");
Uri contentUri = getUriForFile(getContext(), "org.cryse.fileprovider", newFile);

赋予权限

赋予权限步骤如下:
调用 Context.grantUriPermission(package, Uri, mode_flags), 然后用 Intent.setData() 将 Uri 放入 Intent, 接下来通过 Intent.addFlags(), 根据需要赋予 FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_READ_URI_PERMISSION, 最后将 Intent 发送。

final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mime);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
mContext.startActivity(intent);

发送给其他应用

根据目标类型,按照正常的 Intent 使用。

Context.startActivity(intent);
Context.startService(intent);

FileProvider 的问题

通过 FileProvider 提供的文件的 schema 是 content://,而由于部分 Uri 作为参数的地方,只支持 schema 为 file://Uri, 所以需要避免将这类文件保存在应用私有目录中,而是应该保存在 Internal/External 目录中。
例如在 Android N DP4 之前的系统上, *.apk 的安装包就无法通过 FileProvider 使用, 直到 Android N Developer Preview 4 修复了这个问题 https://code.google.com/p/android/issues/detail?id=205827

发表评论

电子邮件地址不会被公开。 必填项已用*标注