原文首发于微信公众号:躬行之,欢迎关注交流!java
开发中常常须要将某个文件向另外一个应用程序传递,如图片上传到另外一个应用程序、文件在不一样存储路径之间的复制粘贴等都须要共享文件,能够这样理解接收文件的应用是在向提供文件的应用发送请求。android
从 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 系统在安全性上不断完善的结果。app
在 AndroidManifest 文件中指定 Provider,参考以下:ide
<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 中定义的路径有如下几种,具体参考以下:ui
<?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 后面做为该文件的访问路径,参考以下:code
//表示当前要共享的文件会在 context.getFileDir() 目录下的 images 子目录下查找要共享的文件 <paths> <files-path path="images/" name="myImage" /> </paths> //表示最终生成的共享的文件URL content://com.example.myapp.fileprovider/myImage/image.jpg
最后,配置完成以后,在全部须要使用文件相关的,在获取 Url 时应该按照以下方式获取,具体以下:xml
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 以上愉快的共享文件了,这个知识点也算是开发中常常会遇到到。图片