android 7.0应用间共享文件----FileProvider
2017-12-09 15:41
381 查看
应用间经常需要将自己的私有文件共享给其他的app,如某应用想要共享图库的图片用来编辑用户的头像,或者文件管理app允许用户在不同目录下复制粘贴文件等等,但为了保护私有文件的安全性,在targetSdk版本为N或者以后版本的app中,应用的私有目录被限制访问。。。
面向Android7.0的应用,android框架执行的StrictMode API政策禁止在您的应用外部公开file://URI。如果一项包含文件URI的intent离开您的应用,则您的应用会停止运行,抛出FileUriExposedException
如果要在文件间共享文件,唯一安全的方式就是要发送一项content://URI,并授予URI临时访问权限。这个方式之所以安全,是因为只适用于收到这个URI的应用,且会自动过期。进行此授权最简单的方式就是使用FileProvider类
接下来概述下如何使用FileProvider
FileProvider也是Android四大基本组件之一,直接在manifest中声明一个FileProvider,如下
name=”photo” (A URI path segment)为了加强安全性,name定义的值隐藏了你真正要分享的子目录的名字,子目录的名字由path属性定义
path=”images” (The subdirectory you’re sharing你要分享的子目录)
name定义的值是content uri中的路径名,path定义的值是真正子目录的名字
注意:path定义的是一个子目录,而不是一个或多个私密文件,你不能通过文件名,也不能通过文件的子集(用通配符)共享一个文件
paths元素中可包括一个或多个子元素
上述例子对应的file 文件目录:/data/user/0/com.example.fileproviderdemo/files/images/myself
FileProvider生成的Content Uri是content://com.example.fileproviderdemo.fileprovider/photo/myself
(content://android:authorities/name/fileName)
方法1: 调用Context.grantUriPermission(package, Uri, mode_flags) 这个方式是只会授权给指定包名的app,权限的有效期:手动调用 Context#revokeUriPermission(Uri uri, int modeFlags)撤销授权,或者重启手机列表内容
方法2: 调用Intent#setFlags (int flags)或这Intent#addFlags (int flags),权限的有效期:当接受到的activity处于活跃状态时持续有效,退出则自动失效,一个activity获取到Content Uri的临时权限,这个权限会延展至这个应用的其他组件
方法1和方法2 中提到的flag均是Intent#FLAG_GRANT_READ_URI_PERMISSION 或Intent# FLAG_GRANT_WRITE_URI_PERMISSION
附上测试demo: https://pan.baidu.com/s/1gfMXILH
参考文档:https://developer.android.com/reference/android/support/v4/content/FileProvider.html
https://developer.android.com/about/versions/nougat/android-7.0-changes.html
https://developer.android.com/training/secure-file-sharing/index.html
面向Android7.0的应用,android框架执行的StrictMode API政策禁止在您的应用外部公开file://URI。如果一项包含文件URI的intent离开您的应用,则您的应用会停止运行,抛出FileUriExposedException
12-09 01:44:45.284 E/AndroidRuntime(17911): FATAL EXCEPTION: main 12-09 01:44:45.284 E/AndroidRuntime(17911): Process: com.example.fileproviderdemo, PID: 17911 12-09 01:44:45.284 E/AndroidRuntime(17911): android.os.FileUriExposedException: file:///data/user/0/com.example.fileproviderdemo/files/image exposed beyond app through ClipData.Item.getUri() 12-09 01:44:45.284 E/AndroidRuntime(17911): at android.os.StrictMode.onFileUriExposed(StrictMode.java:1958) ... 12-09 01:44:45.290 W/ActivityManager( 1148): Force finishing activity com.example.fileproviderdemo/.MainActivity
如果要在文件间共享文件,唯一安全的方式就是要发送一项content://URI,并授予URI临时访问权限。这个方式之所以安全,是因为只适用于收到这个URI的应用,且会自动过期。进行此授权最简单的方式就是使用FileProvider类
FileProvider共享文件
FileProvider是ContentProvider的一个特定的子类,通过创建content://类型的uri来替代file://类型的URI,从而为一个app提供了更加安全的文件分享操作。接下来概述下如何使用FileProvider
在AndroidManifest.xml中定义FileProvider
FileProvider默认提供了为文件创建content Uri的功能,因此就不必再在代码中定义一个她的子类了。FileProvider也是Android四大基本组件之一,直接在manifest中声明一个FileProvider,如下
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.fileproviderdemo"> <application> ... <provider android:authorities="com.example.filepandroid:authoritiesroviderdemo.fileprovider"//content uri android:name="android.support.v4.content.FileProvider" android:grantUriPermissions="true"//设置为true,才可授予临时权限 android:exported="false">//不需要暴露给外部,设置为false即可 <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>//共享的文件目录配置,下一小结具体介绍 </provider> ... </application> </manifest>
定义可共享的文件目录
FileProvider只会为提前指定号的文件目录生成content URI,通过在xml文件中,以元素指定的文件目录,如下面可为files/images目录下的文件生成content URI:<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="photo" path="images/"/> </paths>
name=”photo” (A URI path segment)为了加强安全性,name定义的值隐藏了你真正要分享的子目录的名字,子目录的名字由path属性定义
path=”images” (The subdirectory you’re sharing你要分享的子目录)
name定义的值是content uri中的路径名,path定义的值是真正子目录的名字
注意:path定义的是一个子目录,而不是一个或多个私密文件,你不能通过文件名,也不能通过文件的子集(用通配符)共享一个文件
paths元素中可包括一个或多个子元素
<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="name" path="path"/> // files/文件子目录,可通过Context#getFilesDir()获取 <cache-path name="name" path="path" />// cache/文件子目录,可通过Context#getCacheDir()获取 <external-path name="name" path="path" />// Environment.getExternalStorageDirectory() <external-files-path name="name" path="path" />// Context#getExternalFilesDir(String) <external-cache-path name="name" path="path" />// Context#getExternalCacheDir() </paths>
生成文件的Content Uri
调用FileProvider#getUriForFile()生成文件的Content UriFile filePath = new File(getFilesDir(), "images"); File file = new File(filePath, "myself"); Uri contentUri = FileProvider.getUriForFile(this, "com.example.fileproviderdemo.fileprovider", file);
上述例子对应的file 文件目录:/data/user/0/com.example.fileproviderdemo/files/images/myself
FileProvider生成的Content Uri是content://com.example.fileproviderdemo.fileprovider/photo/myself
(content://android:authorities/name/fileName)
给content uri授予临时权限
有两种为Content Uri授予临时权限的方式:方法1: 调用Context.grantUriPermission(package, Uri, mode_flags) 这个方式是只会授权给指定包名的app,权限的有效期:手动调用 Context#revokeUriPermission(Uri uri, int modeFlags)撤销授权,或者重启手机列表内容
//功能,对目标文件进行裁剪,裁剪完并复制到输出文件 //inputUri是要裁剪的目标文件,outUri是裁剪后的输出文件 //Intent中有要传输两个Uri,必须用grantUriPermission方法,方法2只适用于一个uri的情况 Intent intent = new Intent(ACTION_CROP); intent.setDataAndType(inputUri, "image/"); intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri); List<ResolveInfo> infoList = getPackageManager().queryIntentActivities(intent, 0); for (ResolveInfo resolveInfo: infoList) { final String packageName = resolveInfo.activityInfo.packageName; grantUriPermission(packageName, inputUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); grantUriPermission(packageName, outUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } startActivityForResult(intent, CODE_REQUEST_CROP);
方法2: 调用Intent#setFlags (int flags)或这Intent#addFlags (int flags),权限的有效期:当接受到的activity处于活跃状态时持续有效,退出则自动失效,一个activity获取到Content Uri的临时权限,这个权限会延展至这个应用的其他组件
//功能:拍照,并将照片复制到输出文件中 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivityForResult(intent, CODE_REQUEST_TAKE_PHOTO);
方法1和方法2 中提到的flag均是Intent#FLAG_GRANT_READ_URI_PERMISSION 或Intent# FLAG_GRANT_WRITE_URI_PERMISSION
将Content Uri共享给其他应用
将Content Uri分享给其他应用,除了上个步骤中的例子通过setData的方案,也可以调用Intent#setClipData() ,只不过这个方法是android sdk 16才开始支持的附上测试demo: https://pan.baidu.com/s/1gfMXILH
参考文档:https://developer.android.com/reference/android/support/v4/content/FileProvider.html
https://developer.android.com/about/versions/nougat/android-7.0-changes.html
https://developer.android.com/training/secure-file-sharing/index.html
相关文章推荐
- Android 7.0 通过FileProvider实现应用间文件共享
- Android 7.0 行为变更 通过FileProvider在应用间共享文件吧
- Android N 7.0 应用间共享文件(FileProvider)
- Android 7.0 行为变更 通过FileProvider 在应用间共享文件
- Android 7.0 行为变更 通过FileProvider在应用间共享文件
- Android 7.0 行为变更 通过FileProvider在应用间共享文件
- android 7.0 使用FileProvider在应用间共享文件(相机适配)
- Android 7.0 适配-应用之间共享文件(FileProvider)
- Android 7.0配置fileprovider共享文件 解决FileUriExposedException
- android笔记-7.0App间共享文件
- Android 7.0 行为变更 通过FileProvider在应用间共享文件吧
- 应用间共享文件 FileProvider
- android 7.0以上共享文件(解决调用系统照相和图片剪切出现的FileUriExposedException崩溃问题)
- Android 7.0 行为变更 通过FileProvider在应用间共享文件吧
- Google Android开发者文档系列-创建有内容分享特性的应用之文件共享(序言)
- Android 7.0 应用间文件访问方法(FileProvider)
- Google Android开发者文档系列-创建有内容分享特性的应用之共享文件
- android 7.0以上共享文件(解决调用系统照相和图片剪切出现的FileUriExposedException崩溃问题)
- Android 7.0 行为变更 通过FileProvider在应用间共享文件吧
- Android 7.0 行为变更 通过FileProvider在应用间共享文件吧