FileProvider
在Android7.0及之后我们无法直接将一个FileUri共享给另一个程序进行使用。系统会抛出一个异常FileUriExposedException。官方是这样描述的:
The exception that is thrown when an application exposes a file://
Uri
to another app.
当一个应用程序暴漏一个file://
Uri
给另一个app时就会抛出这个异常。
This exposure is discouraged since the receiving app may not have access to the shared path. For example, the receiving app may not have requested the Manifest.permission.READ_EXTERNAL_STORAGE
runtime permission, or the platform may be sharing the Uri
across user profile boundaries.
由于需要接收fileURI的应用程序可能无法访问共享的路径,因此不建议这样做。这可能是由于使用了Manifest.permission.READ_EXTERNAL_STORAGE
权限导致,或者平台可以跨越用户配置边界共享Uri。
PS:这个很好理解,比如说我有一个app被装在了手机上,但是没有申请READ_EXTERNAL_STORAGE权限(6.0后需要动态申请),但是我在另一个程序中请求这个app来读取这个文件是不是就会出现问题了,肯定就会出现异常了。所以说使用了内容提供程序,数据的读取是由内容提供者进行读取的,这样就要求数据提供者必须具有这个权限,也保证了数据安全。
Instead, apps should use content://
Uris so the platform can extend temporary permission for the receiving app to access the resource.
我们应该使用content://
Uris对其进行替换,以便平台可以为需要访问特定资源的app扩展临时权限。
This is only thrown for applications targeting Build.VERSION_CODES#N
or higher. Applications targeting earlier SDK versions are allowed to share file://
Uri
, but it’s strongly discouraged.
这个异常只会在目标版本大于等于7.0时抛出。之前的版本可以继续使用fileURI,不过不推荐这样做。
这些都是由于7.0开启了严格模式(StrictMode)造成的,官方在7.0的变更中是这么说的:
对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode
API 政策禁止在您的应用外部公开 file://
URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException
异常。
FileProvider类的继承关系
java.lang.Object
android.content.ContentProvider
android.support.v4.content.FileProvider
官方介绍
FileProvider
is a special subclass of ContentProvider
that facilitates secure sharing of files associated with an app by creating a content://
Uri
for a file instead of a file:///
Uri
.
FileProvider
是ContentProvider
的子类,它通过为一个文件创建content://
Uri
来替换file:///
Uri
,以此来达到文件的安全共享。
核心步骤
1、定义FileProvider
2、定义可用的文件路径
3、为定义的FileProvider添加文件路径
4、为特定文件生成ContentURI
5、授予ContentURI授予临时权限
1、定义FileProvider
由于FileProvider提供了ContentURI的生成方法,所以我们无需在代码中定义写一个它的子类。以下代码中的name属性是固定的,authorities可以自己定义,一般是包名字加上.fileprovider。exported设置为false,因为通常是拒绝外部直接访问的。grantUriPermissions需要为true,需要授予临时的Uri权限。
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
...
</provider>
...
</application>
</manifest>
2、定义可用的文件路径
FileProvider
只能为预先指定的目录中的文件生成可用的ContentURI。要指定目录,需要使用<paths>
该文件需要建立在res目录下名为xml的目录下,xml目录需要自己建立。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--定义APP的存放目录-->
<external-path
name="AppInstaller"
path="/Download"></external-path>
</paths>
paths
下可以包含一个或者多个子节点。
<root-path/> 代表设备的根目录new File("/");//很少用
//app内部存储
<files-path/> 代表context.getFilesDir()
<cache-path/> 代表context.getCacheDir()
//sd卡存储
<external-path/> 代表Environment.getExternalStora