android开发中关于7.0适配的FileProvider的总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33234564/article/details/79447518

android开发中关于7.0适配的FileProvider的总结

问题描述—-Android7.0权限变更的介绍

android7.0在安全性能上可以说是做了升级的。主要表现是应用的私有目录的访问被限制。开发者不能简单的通过file:// URI访问其他应用的私有目录或者其他应用访问本应用的私有目录。并且Android SDK 中的StrictMode 策略禁止开发人员在应用外部公开 file:// URI。当我们在应用中使用包含 file:// URI 的 Intent 离开自己的应用时,程序就会发生闪退现象。

开发中的实际场景介绍

  1. 打开相机

    `String filePath = Environment.getExternalStorageDirectory() + "/images/"+System.currentTimeMillis()+".jpg";
    File outputFile = new File(filePath);
    if (!outputFile.getParentFile().exists()) {
        outputFile.getParentFile().mkdir();
    }
    Uri contentUri = FileProvider.getUriForFile(this,
        BuildConfig.APPLICATION_ID + ".myprovider", outputFile);
    
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
    startActivityForResult(intent, REQUEST_TAKE_PICTURE);`
    
  2. 应用更新时安装应用

    首先需要在res/xml 目录下新建一个 file_paths.xml 文件(当然此文件名自由定义),并添加子目录路径信息:

    <resources>
    <paths>
        <external-path
            name="files_root"
            path="AutoServe/Update/com.app.myapp/"/>
        <external-path
            name="external_storage_root"
            path="."/>
    </paths>
    </resources>
    

    然后在清单文件中注册FileProvider,并链接上边的path

    <provider
                        android:name="android.support.v4.content.FileProvider"
                    android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                        android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    

最后的java代码

File apkfile = new File(apkFilePath);
    if (!apkfile.exists()) {
        return;
    }

    Intent intent = new Intent(Intent.ACTION_VIEW);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        String authority = mContext.getPackageName() + ".provider";
        Uri contentUri = FileProvider.getUriForFile(mContext,authority,new File(apkFilePath));
        intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
    } else {
        intent.setDataAndType(Uri.fromFile(new File(apkFilePath)), "application/vnd.android.package-archive");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }
    mContext.startActivity(intent);

原理介绍

FileProvider是ContentProvider的一个子类,我们都知道ContentProvider扮演着数据共享的角色,而FileProvider就是帮助我们将限制访问的 file:// URI 转化为授权共享的content:// URI

  1. 注册FileProvider

在清单文件的下

    <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">

        </provider>

其中android:authorities 属性值是一个由 build.gradle 文件中的 applicationId 值和自定义的名称组成的 Uri 字符串。其他属性值是固定的。

  1. 添加共享目录

    在 res/xml 目录下新建一个 xml 文件,用于存放应用需要共享的目录文件。具体代码参考上边安装应用时候的file_paths

    其中子元素是为了指定共享文件的目录路径,子元素必须为以下元素之一

    • :内部存储空间应用私有目录下的 files/ 目录,等同于 Context.getFilesDir() 所获取的目录路径;
    • :内部存储空间应用私有目录下的 cache/ 目录,等同于 Context.getCacheDir() 所获取的目录路径;
    • :外部存储空间根目录,等同于 Environment.getExternalStorageDirectory() 所获取的目录路径;
    • :外部存储空间应用私有目录下的 files/ 目录,等同于 Context.getExternalFilesDir(null) 所获取的目录路径;
    • :外部存储空间应用私有目录下的 cache/ 目录,等同于 Context.getExternalCacheDir();

添加完共享目录后,再回到清单文件,在provider中通过mate标签将path与provider连接起来。代码如下:

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
  1. 转化为Content URI

在 Android 7.0 出现之前,我们通常使用 Uri.fromFile() 方法生成一个 File URI。这里,我们需要使用 FileProvider 类提供的公有静态方法 getUriForFile 生成 Content URI。比如:

Uri contentUri = FileProvider.getUriForFile(this,
            BuildConfig.APPLICATION_ID + ".myprovider", myFile);

需要传递三个参数。第二个参数便是 Manifest 文件中注册 FileProvider 时设置的 authorities 属性值,第三个参数为要共享的文件,并且这个文件一定位于第二步我们在 path 文件中添加的子目录里面。

  1. 授权访问

生成 Content URI 对象后,需要对其授权访问权限。授权方式有两种:

第一种方式,使用 Context 提供的 grantUriPermission(package, Uri, mode_flags) 方法向其他应用授权访问 URI 对象。三个参数分别表示授权访问 URI 对象的其他应用包名,授权访问的 Uri 对象,和授权类型。其中,授权类型为 Intent 类提供的读写类型常量:

FLAG_GRANT_READ_URI_PERMISSION

FLAG_GRANT_WRITE_URI_PERMISSION

或者二者同时授权。这种形式的授权方式,权限有效期截止至发生设备重启或者手动调用 revokeUriPermission() 方法撤销授权时。

第二种方式,配合 Intent 使用。通过 setData() 方法向 intent 对象添加 Content URI。然后使用 setFlags() 或者 addFlags() 方法设置读写权限,可选常量值同上。这种形式的授权方式,权限有效期截止至其它应用所处的堆栈销毁,并且一旦授权给某一个组件后,该应用的其它组件拥有相同的访问权限。

  1. 提供 Content URI 给其它应用

拥有授予权限的 Content URI 后,便可以通过 startActivity() 或者 setResult() 方法启动其他应用并传递授权过的 Content URI 数据。当然,也有其他方式提供服务。

如果你需要一次性传递多个 URI 对象,可以使用 intent 对象提供的 setClipData() 方法,并且 setFlags() 方法设置的权限适用于所有 Content URIs。

参考:http://blog.csdn.net/growing_tree/article/details/71190741

展开阅读全文

没有更多推荐了,返回首页