Android N 文件分享 - litonghui/TechBlog GitHub Wiki

对于软件分享功能,例如分享图片到微信好友或者朋友圈,Android提过几种方式,content://Uri 和 file://Uri通过Intent,最新的Android N 7.0 不再允许在app中把file://Uri暴露给其他app,包括但不局限于通过Intent或ClipData 等方法。关于文档中的描述

原因是file://Uri存在一定风险

1. 文件是私有的,接收file://Uri的app无法访问该文件。
2. 在Android6.0之后引入运行时权限,如果接收file://Uri的app没有申请READ_EXTERNAL_STORAGE权限,在读取文件时会引发崩溃。

解决方案,用FileProvider 的content:// Uri 代替

在AndroidManifest.xml的节点中添加

<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/provider_paths" />
其中 res/xml/provider_paths.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path  path="." name="root" />
    <files-path  path="images/" name="my_images" />
    <files-path  path="audios/" name="my_audios" />
    ...
</paths>

paths中可以定义以下子节点

子节点	对应路径
files-path	         Context.getFilesDir()
cache-path	         Context.getCacheDir()
external-path            Environment.getExternalStorageDirectory()
external-files-path	 Context.getExternalFilesDir(null)
external-cache-path	 Context.getExternalCacheDir()

生成 Content URI 方式如下:

File imagePath = new File(getContext().getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.provider", newFile);

最后生成的 Content URI 为

content://com.domain.example.provider/images/default_image.jpg.

代码示例:

Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

// 系统版本大于N的统一用FileProvider处理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

    // 将文件转换成content://Uri的形式
    Uri photoURI = FileProvider.getUriForFile(activity,
            activity.getPackageName()+ ".provider",
            new File(photoPath));

    // 申请临时访问权限
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
} else {
    intent.addCategory(Intent.CATEGORY_DEFAULT);
    Uri uri = Uri.parse("file://" + photoPath);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
}
activity.startActivityForResult(intent, requestCode);
⚠️ **GitHub.com Fallback** ⚠️