自己简单的翻译了一下整篇文档,然后和网络上对比了一下,感觉还是太菜,所以转了其他人的翻译,感觉更适合。稍微修改了一下个别错别字,英文留空有空再改。
参考:http://www.bendaidai.top/?p=100
FileProvider 是 ContentProvider ( 有助于 App 共享文件更加安全的组件 )的一个特殊的子类,通过 content:// Uri 获取一个文件而不是 file:/// Uri。 当你创建一个包含有 content URI 的 Intent 的时候,Content URI赋予你临时的读写权限。为了可以给一个目标app( 原文client app,这里应该是目标App )发送一个特定的content URI,你也可以通过调用Intent.setFlags() 去添加权限,这些权限一直有效,只要接收的 Activity 在栈中还存活着。要是跳转到Service,只要这个Service在运行,权限就有效。相比较而言,用file:/// Uri来获取文件,你不得不修改系统底层的文件权限.在你改变文件权限之前,你授予权限,对于任何app都是可用的,所以授予这个级别的权限从根本上是不安全的.(就是liunx的rwx) content URI提高了文件安全访问的级别,使FileProvider成为Android的安全体系重要组成部分。
FileProvider 可大致分为下面几个部分:
- 1.自定义一个FileProvider
- 2.指定可用文件
- 3.给文件生成content URI
- 4.授予URI一个临时权限
- 5.给另一个App提供URI
1.自定义一个FileProvider
因为FileProvider的默认功能已经包含了将一个文件生成Content URI,所以,你不需要再在你的代码里去定义一个子类.相应的,你完全可以在你App的XML中通过 元素去指定一个FileProvider,包括指定FileProvider的组件,给 android.support.v4.content.FileProvider设置 android:name属性,基于你的控制域给 android:authorities设置一个属性.例如,如果你控制mydomain.com域,你应该用com.mydomain.fileprovider授权.设置 android:exported属性为false; FileProvider不需要成为一个共有的类,设置android:grantUriPermissions这个属性为true,则允许你给文件临时授权.例子
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
...
</provider>
...
</application>
</manifest>
如果你想要重写FileProvider中默认的方法,请继承FileProvider,并且要在 元素下的android:name写上全类名
2.指定可用文件
一个FileProvide仅仅只能为你生成一个预先在目录中指定的文件的content URI,在XML中,用的子元素去指定一个目录,并指定它的存储区域和路径.例如,下面这个paths元素告诉FileProvider,你打算去访问你的images/子目录下面私有文件的content URI
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>
元素必须有一个或多个下面类似的子标签 它表示你的app的内部存储区根目录的文件,该子目录的根路径等同于Context.getFileDir
<cache-path name="name" path="path" />
它表示你的app的内部缓存区根目录的文件,该子目录的根路径等同于getCacheDir()
<external-path name="name" path="path" />
它表示你app的外部缓存区根目录的文件,该子目录的根路径等同于Environment.getExternalStorageDirectory().
<external-files-path name="name" path="path" />
它表示你的app的外部存储区根目录的文件,该子目录的根路径等同于Context.getExternalFilesDir(null).
<external-cache-path name="name" path="path" />
它表示你的app的外部缓存区根目录的文件,该子目录的根路径等同于Context.getExternalCacheDir().
这些子元素都有相同的属性
name="name"
一个URI路径段,为了确保安全,这个值隐藏了你将要分享的子目录的名字,这个子目录的名字被包含在path的属性值中.
path ="path"
你将要分享的子目录,虽然name属性是一个URI路径片段,但是path的值是一个真正的路径名字.注意这个值只能引用一个子目录,不能是一个或多个文件,你不能通过文件名单独的分享一个文件,也不能用通配符来指定某一类型的文件
你必须为你想要content URI文件的每个目录指定子元素.例如,这写xnl元素指定了两个目录
<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>
把<paths>元素和它的子元素放到你项目中的一个xml中,例如,你可以把它们添加到一个叫 res/xml/file_paths.xml 的文件中,为了让它和 FileProvider 联系起来,在定义好FileProvider的元素下添加一个 <meta-data> 做为 FileProvider
的子元素,并设置 "android:name" 属性为 android.support.FILE_PROVIDER_PATHS。设置 "android:resource" 属性为 @xml/file_paths (注意不要指定.xml扩展名).例如
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
3.给文件生成content URI
为了给另一个app通过content URI分享文件,因此,你的app不得不生成一个content URI.为了生成content URI,首先要给文件new File,然后传递File 到getUriForFile(),你可以通过 把getUriForFile()返回的content URI放在Intent中传递给另一个app,目标app收到这个content URI就能打开这个文件并且通过调用 ContentResolver.openFileDescriptor得到ParcelFileDescriptor进而读取它的内容.
例如,假设你的app正要通过拥有 com.mydomain.fileprovider权限的FileProvider给另一个app提供文件,你可以通过下面的代码去得到一个在images/ 子目录下的default_image.jpg的content URI:
File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
由于先前片段截取的结果,getUriForFile()返回这样一个结果: content://com.mydomain.fileprovider/my_images/default_image.jpg
4.授予URI一个临时权限
想要授予从getUriForFile()获取content URI一个访问权限,可以通过下面任意一种方式:
- 1.把content:// Uri传入 Context.grantUriPermission(package, Uri, mode_flags) 方法中,用你想要的模式标签,这将授予content URI指定的包临时访问权限,你可以把mode_flags参数的值设置为 FLAG_GRANT_READ_URI_PERMISSION,FLAG_GRANT_WRITE_URI_PERMISSION ,或者都设置上.这个授权将在你调用revokeUriPermission()或者重启设置之前一直有效.
- 2.通过Intent调用setData()把content URI放进去
- 3.接下来,调用Intent.setFlags()设置为FLAG_GRANT_READ_URI_PERMISSION,FLAG_GRANT_WRITE_URI_PERMISSION,至少一个.
- 4.最后,发送Intent到另一个app,大多数情况下,你可通过调用setResult()来实现它.
- 5.在接受方Activity在栈中存活的情况下,被赋予Intent权限中仍然有效.当栈被销毁的时候,权限将自动被移除.权限将自动扩展到该app的其他组件,并授权给其中的一个Activity.
5.给另一个App提供URI
有各种各样的方式去吧一个文件的content URI提供给目标app.一个通用的方式是通过调用startActivityResult()有目标app启动你的app,目标app给你的app发送一个Intent去启动你的一个Activity,对此,你的app可以及时的给目标app返回一个content URI或者提供一个界面供用户选择文件.在最后一种情况下,一旦用户选择了文件,你的app就能返回它的content URI.以上联众情况,你的app都可以通过setREsult()发送包含有content URI的Intent
你也可以把content URI放到ClipData对象中,然后把对象添加到Intent中发送给你的目标app.要做到这一点,调用Intent.setClipData().当你用这个方法的时候,你能添加多种ClipData对象给Intent,每一个都有自己的content URI,当你调用通过Intent调用Intent.setFlags()设置临时权限的时候,所有的content URI都将有相同的权限.
注:该Intent.setClipData()方法只适用于平台版本16(安卓4.1)及更高版本。如果你想保持与以前版本的兼容性,你应该在一个时间发送一个内容URI Intent。设置行动ACTION_SEND,并通过调用把URI数据 setData()。
网友评论