android7.0之后版本,FileProvider适配问题

前言:这是我的第一篇博客,之前一直有写博客的想法,不知道从何下手而一直往后拖,近来因为一些原因越来越意识到自己的知识的不足,终于下定决心弥补,希望通过坚持写博客能给自己一些提高。本文是前两个月遇到的情况,更新APP的时候在一些系统7.0以上的手机会崩溃,所以花了一上午的时间研究,在此再做记录。

概述
android7.0中有一些新的变化,(官方文档),例如多窗口的支持,FileProvider等等,今天我们只对FileProvider做讲解,这也是我们在开发过程中必须要做的适配。

在7.0以上系统的手机上,Android框架执行的StrictMode API政策禁止在应用外部公开file//URI;传递file://URI会报出FileUriExposedException异常。如果要在应用间共享文件,应发送一项content//URI,最简单的方式就是使用FileProvider类。

使用方法
1,在ManiFest中的的节点中注册一个FileProvider,因为FileProvider是ContentProvider的子类;
android7.0之后版本,FileProvider适配问题_第1张图片

  • name:配置当前FileProvider的实现类,一般为固定的。也可以写继承FileProvider的实现类
  • authorities:配置一个FileProviderde 名字,必须是当前系统内的唯一值,一般为【包名+字符串】(例:com.example.demo.customName,如上图格式会自动引用gradle中的applicationId)
  • exported:表示该FileProvider是否需要公开,一般不需要,本人测试:必须false否则抛异常
  • grantUriPermissions:是否允许授权的文件临时访问权限,本人测试:必须true否则抛异常

2,配置可分享的文件的路径

name:是固定的,
resource:需要在res目录下新建xml文件夹,这里指向xml文件夹下的provider_paths文件,此文件的根节点为paths;
paths 标签内,必须配置最少一个 xxx-path 标签

//path=""---------表示为当前指定目录 如下:
<paths>
<root-path name="root" path="" /> //root-path:表示根目录,『/』。
<files-path name="files" path="" /> //files-path:表示 content.getFileDir() 获取到的目录。
<cache-path name="cache" path="" /> //files-path:表示 content.getFileDir() 获取到的目录。
<external-path name="external" path="" />  //files-path:表示 content.getFileDir() 获取到的目录。
<external-files-path name="name" path="" />  //files-path:表示 content.getFileDir() 获取到的目录。
<external-cache-path name="name" path="" />  //external-cache-path:表示 ContextCompat.getExternalCacheDirs() 获取到的目录
</paths>

3,使用content:// 调用FileProvider.getUriForFile()

   /** @param context     上下文对象
     * @param authority   这里是前面在 AndroidManifest.xml 中 配置的 android:authorities 。
     * @param file        要获取URI的file对象
     */

Fileprovider.getUriForFile(Context context,String authority,File file) 
// 调用此方法,会自动得到一个 file:// 转换成 content:// 的 一个 Uri 对象,可以供我们直接使用。
生成的 Content URI 是这样的

content://com.example.demo.customName/*****

host 部分为  元素的 authorities 属性值(applicationId + customname),
path 片段为 res/xml 文件中指定的子目录别名

4,授予临时读写的权限

在配置 provider 标签的时候,有一个属性 android:grantUriPermissions=”true” ,它表示允许它授予 Uri 临时的权限。
当我们生成出一个 content:// 的 Uri 对象之后,其实也无法对其直接使用,还需要对这个 Uri 接收的 App 赋予对应的权限才可以。
授权类型的常量,被定义在 Intent 类中。

授权的方式分为两种:

  • 使用Context.grantUriPermission() 为其他 App 授予 Uri 对象的访问权限。
/****
     * 
     * @param toPackage  表示授予权限的 App 的包名。
     * @param uri        授予权限的 content:// 的 Uri。
     * @param modeFlags  读写权限。
     */
 @Override
    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
        super.grantUriPermission(toPackage, uri, modeFlags);
    }
//这种情况下,授权的有效期限,从授权一刻开始,截止于设备重启或者手动调用 Context.revokeUriPermission() 方法,才会收回对此 Uri 的授权。

示例代码:(此处借鉴鸿洋大神的图片 博客—->)
android7.0之后版本,FileProvider适配问题_第2张图片

  • 配合 Intent.addFlags() 授权。 setFlags() == addflags()

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    示例代码:

 Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction("android.intent.action.VIEW");
        intent.addCategory("android.intent.category.DEFAULT");
        Uri data;
        // 判断版本大于等于7.0
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
       data = FileProvider.getUriForFile(context,"com.example.demo.fileprovider", file);
       intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |  
                       Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
      } else {
            data = Uri.fromFile(file);
      }

一旦授权,直到该 App 被完全退出,这段时间内,该 App 享有对此 Uri 指向的文件的对应权限,我们无法再主动收回此权限

常见使用场景

1,自动安装文件
2,调用系统相机拍照
3,调用系统裁剪

总结:终于完成了第一篇技术博客,发现真心不容易,在这里向各位坚持写博客的大佬致敬,本人也会坚持写下去,努力提升自己的水平,不拖android开发工程师的后腿。

由于本人水平有限,代码可能会出现超出本人认知范围的Bug,欢迎各位大佬批评指正。

你可能感兴趣的:(android7.0之后版本,FileProvider适配问题)