天使漫步IT工作室天使漫步IT工作室

android file provider 完全解析


Warning: count(): Parameter must be an array or an object that implements Countable in /www/wwwroot/u11u.com/usr/themes/wq/functions.php on line 110

Warning: count(): Parameter must be an array or an object that implements Countable in /www/wwwroot/u11u.com/usr/themes/wq/functions.php on line 116

一、关于Android 7.0 FileProvider

  • FileProvider是android7.0的产物,但FileProvider并不是最新出来的东西,而是以前就已经存在,由于Android的安全机制 ,一个进程默认不能影响另外一个进程的,如读取私有数据。
  • 那么对于进程间的文件的共享 ,出于安全考虑,用FileProvider。FileProvider会基于manifest中的定义定义的一个xml文件(xml目录 下),为所有定义的文件生成content URIs,这样外部的应用在没有权限的情况下,可以通过授予临时权限的content uri读取相应的文件。
  • FileProvider是v4 support中的类 , 就继承ContentProvider。也就是说content:// Uri 代替了 file:/// Uri. 在Android7.0时候,为了安全,谷歌把它作为了一个强制使用而已。针对file://URI,需要通过FileProvider来转换成content://URI进行访问。

二、那些地方需要FileProvider?

凡是关于文件的访问(写操作)以及使用Intent传递uri的读操作都需要FileProvider。

  • Uri.parse
  • Uri.fromFile
  • file://
  • content://
  • Context.getFilesDir()
  • Environment.getExternalStorageDirectory()
  • getCacheDir()
  • intent.setDataAndType

出现以上这些api或者符号的地方都应该考虑使用FileProvider进行适配,否则会出现以下异常:

java.io.FileNotFoundException: No content provider

java.lang.IllegalArgumentException: Failed to find configured root that contains

uncaughtException:Failed to find configured root that contains

三、如何使用FileProvider

使用FileProvider需要注意三点:在Manifest.xml中定义一个Provider、在一个xml定义允许Provider临时授权的路径(并配置到Manifest.xml中),最后在代码中传入在Manifest.xml中定义的Provider。即:

第一步:在Manifest.xml中定义Provider


        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.xxx.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>

1、其中需要注意的是android:authorities的格式等于app的包名+Provider命名(此命名不区分大小写),例如:

若包名为:com.angel.app,Provider命名为:fileProvider
那么:android:authorities = "com.angel.app.fileProvider"

2、android:resource的值为第二步定义的临时授权路径集合。

第二步:在xml中定义临时授权路径集合。

在res中新建xml文件夹,然后在xml文件夹中新建file_paths.xml文件,在该xml中定义要临时授权的路径。

例如:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    
    <external-path
        name="external_storage_root"
        path="."/>

    <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
    <external-path
        name="beta_external_path"
        path="Download/"/>
    <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
    <external-path
        name="beta_external_files_path"
        path="Android/data/"/>
    <!-- external-path = /storage/emulated/0 -->
    <external-path
        name="files"
        path="Android/data/com.xxx/file/"/> //报名底下的file

    <external-path
        name="external_image_files_path"
        path="DCIM/"/>

    <!-- 代表cache目录下的所有文件-->
    <cache-path
        name="cache_path"
        path="."/>

</paths>

在paths规则下,能够定义的标签有五个,分别是:external-pathexternal-files-pathexternal-cache-pathfiles-pathcache-path

那么这些标签对应那些路径呢?又对应哪些API呢?

1、external-path 对应根目录为/storage/emulated/0的路径,即Environment.getExternalStorageDirectory()获取的路径。
2、cache-path 对应于根目录为getCacheDir() api获取的路径。
3、files-path 对应于根目录为Context.getFilesDir()获取的路径。

以上三个是app最常用的访问路径、其余的符号可以通过查询google文档获知。

回到上面配置,name可以是任意命名,只要xml文件定义域内不冲突即可,path值为在标签根目录下授权的具体路径,如external-path中的path为Download/,那么授权的路径就是:/storage/emulated/0/Download/下所有的文件。

即最终授权的路径为:external-path代表的根路径 + path定义的路径。

第三步:在代码中使用FileProvider

使用FileProvider的代码很简单,只需要在生成uri的时候调用一下FileProvider.getUriForFile 类似的api即可。例如:

// 最后通知图库更新
        Uri contentUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            contentUri = FileProvider.getUriForFile(context, "com.xxx.fileProvider", file);
        } else {
            contentUri = Uri.parse("file://" + file.getAbsolutePath());
        }
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, contentUri));

在getUriForFile中使用到的authority参数即为Manifest中定义的authorities的值(需要和Manifest保持一致)。

上面代码对低版本提供的兼容代码,因为android N以下无需进行额外的适配。

四、最后总结

  • 国内的系统路径定义混乱,有一些系统没有咱们想授权的路径,这个时候可以使用<root-path path="" name="root-path" />进行顶级根目录的全局授权。当然,只是一种偷懒的做法,而且极其不符合FileProvider约束访问设计思想。
  • path的值可以为点符号("."),意思为在该根目录下所有的文件夹都可以临时授权访问,只是比root-path稍微好一点的偷懒做法。
  • 当出现以下异常的时候,需要考虑是否已经正确配置了FileProvider及在代码引用到。笔者还碰到一个文件就是,在低版本没有在文件路径前面给出file://的时候,也出出现此bug。所以要根据android运行的判断定位异常的原因,即android N及以上需要考虑FileProvider的配置正确与否,在android N以下版本需要考虑Uri.parse生成的串是否正确。
java.io.FileNotFoundException: No content provider: /storage/emulated/0/Android/data/

本站原创,欢迎转载,转载敬请标明出处:天使漫步IT工作室 » android file provider 完全解析
添加新评论


Warning: Use of undefined constant php - assumed 'php' (this will throw an Error in a future version of PHP) in /www/wwwroot/u11u.com/usr/themes/wq/comments.php on line 38