开发中经常需要将某个文件向另一个应用程序传递,如图片上传到另一个应用程序、文件在不同存储路径之间的复制粘贴等都需要共享文件,可以这样理解接收文件的应用是在向提供文件的应用发送请求。
从 Android 7.0 开始,Android 执行 StrictMode 策略,禁止在应用外部公开 file://URL,如果在 Android 7.0 以上的应用不使用 FileProvider ,则会抛出 FileUriExposedException 异常,Android 7.0 以后要在应用之间共享文件要使用 content://URL 授予 URL 临时访问权限,即要使用 FileProvider 的方式来授予临时访问权限,具有临时访问权限的 URL 是安全的,这种临时的 URL 会自动过期,其中 FileProvider 提供的 getUriForFile () 用于生成文件的内容。
在所有情况下,从您的应用程序向另一个应用程序提供文件的唯一安全方法是向接收应用程序发送文件的内容 URI,并授予该 URI 的临时访问权限。具有临时 URI 访问权限的内容 URI 是安全的,因为它们仅适用于接收 URI 的应用程序,并且它们会自动过期。 Android FileProvider 组件提供 getUriForFile()方法,用于生成文件的内容 URI。
这里也会提到一个在 Android 7.0 及更高版本时经常出现的异常:FileUriExposedException,通过使用 FileProvider 就可以解决该异常,当然这也是 Android 系统在安全性上不断完善的结果。
- 指定 FileProvider
- 指定文件共享路径
指定 FileProvider#
在 AndroidManifest 文件中指定 Provider,参考如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
...>
<!--android:authorities="${applicationId}.yourname"-->
<provider
android:name="android.support.v4.content.FileProvider"
<!--authorities属性指定要用于FileProvider生成的内容URI的URI权限,一般是applicationId.yourname"组成-->
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>
指定文件共享路径#
上面代码中在 meta-data 目录中指定了要共享的文件目录,文件目录在 filepathd.xml 中定义,可在相应的 xml 中定义的路径有以下几种,具体参考如下:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<!--表示设备的根目录(new File("/"))-->
<root-path name="root" path="" />
<!--表示context.getFileDir()-->
<files-path name="files" path="" />
<!--表示context.getCacheDir()-->
<cache-path name="cache" path="" />
<!--表示Environment.getExternalStorageDirectory()-->
<external-path name="external" path="" />
<!--表示context.getExternalFilesDirs()-->
<external-files-path name="name" path="path" />
<!--表示getExternalCacheDirs()-->
<external-cache-path name="name" path="path" />
</paths>
</resources>
在 xml 中表示某个路径需要两个属性,path 表示当前指定目录的子目录,如果不指定则表示的是当前指定目录下的根目录及子目录,name 表示会将 name 添加的 URL 后面作为该文件的访问路径,参考如下:
//表示当前要共享的文件会在 context.getFileDir() 目录下的 images 子目录下查找要共享的文件
<paths>
<files-path path="images/" name="myImage" />
</paths>
//表示最终生成的共享的文件URL
content://com.example.myapp.fileprovider/myImage/image.jpg
获取 Uri#
最后,配置完成之后,在所有需要使用文件相关的,在获取 Url 时应该按照如下方式获取,具体如下:
public Uri getUri(File file) {
Uri uri = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".youName", file);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
这样就可以在 Android 7.0 以上愉快的共享文件了。