Android N 7.0 应用间共享文件(FileProvider)

来源:互联网 发布:网络剧老炮儿2 编辑:程序博客网 时间:2024/06/10 08:38

Android N 中共享文件

Android N 系统,Android 框架执行的 StrictMode,API 禁止向您的应用外公开 file://URI
如果一项包含文件 URI 的 Intent 离开您的应用,应用会停止运行,并出现 FileUriExposedException异常。

android.os.FileUriExposedException: file:///storage/emulated/0/Download/appName-2.3.0.apk exposed beyond app through Intent.getData()

若要在应用间共享文件,您应发送一项 content://URI,并授予 URI 临时访问权限。
进行此授权的最简单方式是使用 FileProvider类

如何共享文件,简单4步:

1、在AndroidManifest.xml中<application>标签下添加如下代码

<provider    android:name="android.support.v4.content.FileProvider"    android:authorities="自定义的权限名"    android:grantUriPermissions="true"    android:exported="false">    <meta-data        android:name="android.support.FILE_PROVIDER_PATHS"        android:resource="@xml/file_paths" /></provider>

注意:
authorities:权限名称,可自己定义()
grantUriPermissions:必须是true,表示授予 URI 临时访问权限 ( readPermission, writePermission, and permission attributes)
exportedtrue: The provider is available to other applications. false: The provider is not available to other applications.
resource:自定义的xml文件(下面会介绍)

2、在res目录下新建一个xml文件夹,并且新建一个file_paths的xml文件(如下图)

这里写图片描述

3、打开file_paths.xml文件,添加指定的分享目录:

向paths 标签中添加需要的item,item有以下内容:

name:给这个访问路径起个名字

path:需要临时授权访问的路径(.代表所有路径)

path类型:

<files-path/>代表的根目录: Context.getFilesDir()

<external-path/>代表的根目录: Environment.getExternalStorageDirectory()

<cache-path/>代表的根目录: getCacheDir()

<?xml version="1.0" encoding="utf-8"?><paths xmlns:android="http://schemas.android.com/apk/res/android">    <!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/DCIM/camerademo目录-->    <external-path name="DCIM" path="DCIM/camerademo" />    <!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/目录-->    <external-path path="." name="external_storage_root" />    <!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/Pictures/camerademo目录-->    <external-path name="Pictures" path="Pictures/camerademo" />    <!--代表app 私有的存储区域 Context.getFilesDir()目录下的images目录 /data/user/0/com.hm.camerademo/files/images-->    <files-path name="private_files" path="images" />    <!--代表app 私有的存储区域 Context.getCacheDir()目录下的images目录 /data/user/0/com.hm.camerademo/cache/images-->    <cache-path name="private_cache" path="images" />    <!--代表app 外部存储区域根目录下的文件 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)目录下的Pictures目录-->    <!--/storage/emulated/0/Android/data/com.xx.xxxxxx/files/Pictures-->    <external-files-path name="external_files" path="Pictures" />    <!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的images目录-->    <!--/storage/emulated/0/Android/data/com.xx.xxxxxx/cache/images-->    <external-cache-path name="external_cache" path="" /></paths>

4、修改代码适配Android N

Intent intent = new Intent(Intent.ACTION_VIEW);//判断是否是AndroidN以及更高的版本if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {    /* 这句要记得写:这是申请权限,之前因为没有添加这个,打开裁剪页面时,一直提示“无法修改低于50*50像素的图片”,开始还以为是图片的问题呢,结果发现是因为没有添加FLAG_GRANT_READ_URI_PERMISSION。      如果关联了源码,点开FileProvider的getUriForFile()看看,注释就写着需要添加权限。      */    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);    /*getUriForFile(Context context, String authority, File file):此处的authority需要和manifest里面保持一致。以前直接用 Uri.fromFile(apkFile)构建出一个Uri,现在我们使用FileProvider.getUriForFile(); */    Uri contentUri = FileProvider.getUriForFile(context, 自定义的权限名, imageFile);    intent.setDataAndType(contentUri, "application/vnd.android.package-archive");} else {    intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}startActivity(intent);

下面FileProvider的getUriForFile()方法的注释:

       /**          * Return a content URI for a given {@link File}. Specific temporary         * permissions for the content URI can be set with         * {@link Context#grantUriPermission(String, Uri, int)}, or added         * to an {@link Intent} by calling {@link Intent#setData(Uri) setData()} and then         * {@link Intent#setFlags(int) setFlags()}; in both cases, the applicable flags are         * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and         * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. A FileProvider can only return a         * <code>content</code> {@link Uri} for file paths defined in their <code><paths></code>         * meta-data element. See the Class Overview for more information.         *         * @param context A {@link Context} for the current component.         * @param authority The authority of a {@link FileProvider} defined in a         *            {@code <provider>} element in your app's manifest.         * @param file A {@link File} pointing to the filename for which you want a         * <code>content</code> {@link Uri}.         * @return A content URI for the file.         * @throws IllegalArgumentException When the given {@link File} is outside         * the paths supported by the provider.         */        public static Uri getUriForFile(Context context, String authority, File file) {            final PathStrategy strategy = getPathStrategy(context, authority);            return strategy.getUriForFile(file);        }

错误分析

报错

java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg

分析

在生成Uri 的时候,指定的文件所在的路径没有包含在path所指定的路径中

参考:
https://developer.android.com/guide/topics/manifest/provider-element.html

Android7.0须知–应用间共享文件(FileProvider)

解决 Android N 7.0 上 报错:android.os.FileUriExposedException

FileProvider无法获取外置SD卡问题解决方案 | Failed to find configured root that contains

FileProvider 的使用(Failed to find configured root that contains/storage/emulated/0/DCIM/ )

阅读全文
0 0