详解FileProvider的文件共享机制

关于FileProvider文件共享机制及配置共享方法  

尊重作者原创作品,转载请注明出处http://blog.csdn.net/qq_36273967/article/details/72632633

开场白:首先,感谢大家能够来到我的博客观看我的博文,这篇博文也是我写的第一篇博文!好的,先介绍下我自己吧!我学习android开发已经快一年了,说不上是什么大神级别,只能够算一个刚脱掉菜鸟名号的学生,之所以开始写博客,是因为自己在学习android开发这一年里摸爬滚打走了不少的弯路!想把自己在学习及实践中遇到的坑帮助后者填补,让大家避免少走弯路,最后祝大家工作愉快,学业有成。好了废话不多说开始步入正题.  今天我们的主角是FileProvider.首先我为什么介绍FileProvider呢?因为前段时间我在开发项目的时候需要用到一个文件的Uri真实地址,得到Uri后我在真机上测试没有问题,后来我又将项目拉到模拟器上跑,可是程序直接抛出了FileUriExposedException异常,仔细琢磨以后终于知道异常抛出的真相!原来在android7.0以及后续版本如果直接使用Uri.fromFile获得的Uri,系统认为是不安全的,会抛出FileUriExposedException异常,所以我们的主角出常了。  先介绍一下Filprovider,FileProvider是一种特殊的内容提供器,如果还有同学不知道内容提供器的可以去学习ContentProvider这里就不做讲解了。FileProvider有着和ContentProvider相同的机制对数据进行保护,它可以选择性的将封装好了的uri共享给外部从而提升数据的安全。  了解FileProvider以后那么我们怎么才能够获取到我们想要的uri呢?其实很简单,就直接调用这个类的一个静态方法FileProvider.getUriForFile(Context context,String authority,File file);第一个参数是我们的上下文,第二个参数是一个自定义的String字符串,第三个参数就是我们想获取Uri的文件当然我们在实际开发当中规范的写法就是:

if(Build.VERSION.SDK_INT>=24){
     Uri uri = FileProvider.getUriForFile(MainActivity.this,"com.example.administrator.lifehelp.fileProvider",file);
}else{
    Uri uri = Uri.fromFile(file);
}
首先我们要进行判断当前sdk的级别,android7.0对应的就是24,因为这个机制在7.0才出现的,所以我们在7.0版本中使用的getUriForFile方法获取到uri而低于7.0版本的就直接使用Uri.fromFile进行获取
 这样就大功告成了吗?当然没有,因为我们在仔细观察后FileProvider后发现它是一个ContentProvider所以我们还需要在AndroidManifest文件中进行注册

         
在AndroidManifest文件中我们注册了FileProvider,其中android:name指定了注册ContentProvider的包名这里填入我们所使用的FileProvider的包名就可以了,android:authorities用来标识provider的唯一标识,这里填写的值填入我们在调用getUriForFile的第二个参数,第三个参数是否向外导入,就是其他的应用能否使用,竟然我们的FileProvider是为安全机制所生,当然我们不可能让它能够向外导入让其它应用来调用它,这里必须设置false否则就会抛出
Caused by: java.lang.SecurityException: Provider must not be exported
              at android.support.v4.content.FileProvider.attachInfo(FileProvider.java:369)
              at android.app.ActivityThread.installProvider(ActivityThread.java:5633)
              at android.app.ActivityThread.installContentProviders(ActivityThread.java:5205) 
              at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5135) 
              at android.app.ActivityThread.-wrap1(ActivityThread.java) 
              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1646) 
              at android.os.Handler.dispatchMessage(Handler.java:111) 
              at android.os.Looper.loop(Looper.java:207) 
              at android.app.ActivityThread.main(ActivityThread.java:5939) 
              at java.lang.reflect.Method.invoke(Native Method)
第四个参数grantUriPermission授予临时权限,用来控制共享文件的访问权限,竟然不能向外导入,我们就要使用临时权限。meta-data用于配置匹配共享文件的路径,为什么需要设置共享文件的路径呢?因为通过fileProvider给我们返回的uri是进行封装过的,所以我们必须配置它的共享路径才能够解析出来,第一个参数android.support.FILE_PROVIDER_PATHS是固定的,第二个参数是我们引入的xml资源文件,先创建这个资源文件右击res文件->New->Directory命名为xml,之后在这个xml文件夹里面创建一个xml文件,其名自定义:


    
在xml文件中,我们定义了根节点为paths,这里我们需要注意的是子节点,这个节点是用来干嘛的呢?这个子节点表示的是共享的路径,下面我把替换规则放在下面供大家参考:
files-path     Context.getFilesDir() cache-path     Context.getCacheDir() 
external-path     Environment.getExternalStorageDirectory()
external-files-path   Context.getExternalFilesDir(null) external-cache-path     Context.getExternalCacheDir()     
根据自己的项目需求,根据文件存放的位置选择相应的共享位置,否则匹配可能会出错,下面我就把具体的匹配规则写在下面附图让大家更好的去理解fileProvider在匹配中进行的规则
1.将file://替换成content://${android:authorities}
2.匹配和替换
2.1遍历所有的子节点,匹配到最大能够匹配到文件的那个节点
2.2用字节点中name的值替换掉文件匹配中的内容
3.文件剩余的部分将保持不变
这里有必要强调如果子节点中path的值如果是空表示匹配该节点对应路径下的所有文件。




这里在强调几点,我们为什么要设置这个xml。规则就是因为我上面说过了,fileProvider会把文件的真实路径隐藏,会把它转换成content://uri,因此我们需要设定它的转换规则,还有一点就是我们的必须将转换规则的xml文件放在xml文件夹否则会抛出

java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.test/cache/testDemo.txt
好了关于fileProvider的使用就介绍到此处,如果还有疑问的同学欢迎在下面评论,我会及时解答,学习是一种循循渐进的规则,有疑问的地方多读几遍就了解了,最后祝大家学习和工作愉快!

你可能感兴趣的:(android,fileProvider,内容提供器,移动开发)