FileProvider文件共享

来源:互联网 发布:英语网络新兴词汇 编辑:程序博客网 时间:2024/05/29 10:37

本文是在查看Android文档关于拍照与文件共享知识,看到关于FileProvider相关内容后,尝试使用并遇到一些问题时决定做的笔记。

一、FileProvider

1.FileProvider的出现

Android7.0之前,使用file://URI通常会给出该文件的全部访问权限,这是不安全的,应该只有应用自己才有权限。在7.0之后,为了更好的保护用户隐私,不再使用file://URI来提供文件的路径,而是使用content://URI。我们常做的功能中,点击头像拍照,就会用到这个方式。在API23之前,使用file://URI是没问题的,但是24之后就会抛出FileUriExposedException。当然,如果你的应用targetSdkVersion小于24,你仍可以使用这个方法,但是作为开发者,不论是出于产品对Android版本的适配,还是从技术学习角度,都要把app做好。

2.FileProvider使用步骤:

1)定义一个FileProvider

一般来说,我们不需要自定义一个FileProvider,因为Android在supportv4库中已经提供了一个android.support.v4.content.FileProvider类,只需要在manifest进行注册。

<application>     ...     <provider          android:name="android.support.v4.content.FileProvider"          android:authorities="com.example.android.fileprovider"          android:exported="false"          android:grantUriPermissions="true">          <meta-data              android:name="android.support.FILE_PROVIDER_PATHS"              android:resource="@xml/file_paths"></meta-data>      </provider>      ...  </application> 

authorities为基于包名的权限控制,一般就是自己的包名。exported为false,不需要对外公开此provider。grantUriPermission为true,临时授予获取文件权限。meta-data中的resource指定可生成URI目录的描述文件。

2)指定可以获取的文件

FileProvider只可以对预先指定的文件夹内的文件生成content URI.上面的@xml/file_paths即为指定的目录描述。一般是在res的xml文件夹中定义该文件。名称和上面的resource中的配置一致,该例子就是file_paths.xml。

<paths xmlns:android="http://schemas.android.com/apk/res/android">    <files-path name="my_images" path="images/"/>    <files-path name="my_docs" path="docs/"/></paths>
name为生成的contentURI中的代表指定路径的片段,path为可访问的子目录。如上述配置,生成的目录为:content://com.example.android.fileprovider/my_images/文件名。这里有些东西要着重强调,paths内的元素,files-path有很多替换,但是每个都代表了不同的系统文件夹路径,这些会在下面列出。

3)为文件生成URI

File imagePath = new File(Context.getFilesDir(), "images");File newFile = new File(imagePath, "default_image.jpg");Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
之后可以将该URI放在Intent中传递给需要的地方。其他的app可以通过Content.openFileDescription来获取文件描述符。

二、URI生成与匹配遇到的问题

一般来说,主要的问题,在于上面的path与目录的匹配上,匹配不到就会抛出IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/...等异常。下面列出各种path以及匹配的获取系统目录的api。

<files-path name="name" path="path" />
对应Context.getFilesDir();

<cache-path name="name" path="path" />
对应Context.getCahceDir();
<external-path name="name" path="path" />
对应Environment.getExternalStorageDirectory();

这个着重强调一下,因为还支持Enviroment.getExternalStoragePublicDirectory(String)以及Context.getExternalFilesDir(String)。

举个栗子,比如path设置为“Android/data/com.david.study/files/DCIM”。

Contxt.getExternalFilesDir(Enviroment.DIRECTORY_DCIM)可以直接访问到,也就是可以生成URI,因为其文件夹对应的就是指定的目录;Enviroment.getExternalStoragePublicDirectory(Environment.DCIM)无法访问到,因为其目录已经在指定范围外了。Enviroment.getExternalStorageDirectory()无法直接访问到;

new File(Enviroment.getExternalStorageDirectory(), "Android/data/com.david.study/files/DCIM");可以访问到该目录。

如果path仅仅为“DCIM”,则Environment.getExternalPublicStorageDirectory(Environment.DCIM)可以访问,但Context.getExternalFilesDir(Enviroment.DIRECTORY_DCIM)访问不到,而new File(Enviroment.getExternalStorageDirectory(), "DCIM");也可以访问到。

当path=“”时,这三个任何一个都可以访问到。


听起来很乱,举个具体点的栗子

比如说你配置的filepath为


<?xml version="1.0" encoding="utf-8"?>    <paths xmlns:android="http://schemas.android.com/apk/res/android">        <external-path name="my_images" path="Android/data/com.example.app/files/Pictures" />    </paths></xml>


或者


<?xml version="1.0" encoding="utf-8"?>    <paths xmlns:android="http://schemas.android.com/apk/res/android">        <external-files-path name="my_images" path="" />    </paths> </xml>


或者


<?xml version="1.0" encoding="utf-8"?>    <paths xmlns:android="http://schemas.android.com/apk/res/android">        <external-files-path name="my_images" path="Pictures" />    </paths>  </xml>

之后则可以通过getExternalFilesDir(Environment.DIRECTORY_PICTURES);获得到指定的文件夹。注意根节点名称和path的区别。

为何要这么写呢,因为getExternalFilesDir(Environment.DIRECTORY_PICTURES)获取到的目录为Android/data/com.example.app/files/Pictures这个目录因此你在上面的filepath中一点要确保配置的路径能访问到这一层。

分析一下,第一个filepath就不说了,直接指定目录了,完全一致。第二三两个,根节点为external-files-path,对应目录为Android/data/com.example.app/files,只要你指定到files,就可以访问到该目录下所有文件,也包括子文件夹内的东西,当然像第三个还指定了Pictures那就更进一步限制了访问的文件地址。


如果想要访问其他的目录,其实也好处理,可以参考我列出来的几个对应的目录和节点名称,访问对应文件夹。或者要是不知道是哪个目录,你可以先看你用的获取文件夹的方法能访问到哪个目录,之后再来指定其具体的filepath。其实总结起来一句话就是,你用的方法获取的文件夹要和你指定的filepath完全对应,这样才能访问到想要的位置。


<external-files-path name="name" path="path" />
对应Context.getExternalFilesDir(String)或者Context.getExternalFilesDir(null)。

<external-cache-path name="name" path="path" />
对应Context.getExternalCacheDir();

遇到最主要的问题:如果不是对应的目录,会抛出IllegalArgumentException。

解决这个问题的要点是,文件要在该系统目录(files-path等对应的)与path属性对应的目录中。


原创粉丝点击