为什么用FileProvider
7.0和更高的版本使用FilrProvider,这是都知道的事情,但是为什么要使用FilrProvider,因为Android 7.0不允许intent带有file://的URI离开自身的应用了,要不然会抛出FileUriExposedException
想要在自己应用和其他应用之间共享File数据,只能使用content://的方式
(1)建议使用自己的FileProvider
建立自己的FileProvider,因为一个程序中在使用FileProvider时候,可能你调用的第三方的里面也用到了
FileProvider,那么在使用的时候就会存在冲突,建议你在使用时候直接自定义一个:
public class MyFileProvider extends FileProvider {
}
(2)必须在清单文件中配置
在清单文件中注册:
<provider
android:name=".bases.MyFileProvider"
android:authorities="包名.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities"
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/files_names"
tools:replace="android:resource"
/>
</provider>
/***android:name 它的值是固定的,是你的FileProvider,也可以用系统的自带的
android:authorities:属性的值必须要和代码中使用的时候一样。
<meat - data >标签用来指定Uri的共享路径。并且引用了一个@xml/file_paths资源
*/
###(3)
建立资源文件,对应于清单文件中配置的android:resource:属性
在res - New - Directory - xml目录 - New - File
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--填写你要所要申请访问的目录地址,name最好是你的目录名,path是你要申请的目录-->
<external-path name="camera_photos" path="." />
<external-path name="cache" path="Android/data/com.xuezj.fileproviderdemo/cache" />
<external-path name="images" path="Pictures/" />
<external-path name="dcim" path="DCIM/" />
//可以配置多个
</paths>
扩展 paths标签 如何去完成
上述只是演示了外部存储的一种,其实Google还提供以下几种方法
(1)
<files-path name="name" path="path" /> 对应于getFilesDir()所表示的文件路径
(2)
<cache-path name="name" path="path" /> 对应于getCacheDir()所表示的文件路径
(3)
<external-path name="name" path="path" /> 对应于Environment.getExternalStorageDirectory(没有参数)所表示的文件路径,
(4)
<external-files-path name="name" path="path" />
对应于getExternalFilesDir(String) 或者getExternalFilesDir(null) 所表示的文件路径
(5)
<external-cache-path name="name" path="path" /> 对应于getExternalCacheDir()表示的文件路径
(6)
<external-media-path name="name" path="path" /> 对应于Context.getExternalMediaDirs()所表示的文件路径
你可以选择你想要的存储位置,进行选择,对于这些存储位置不懂得,可以参考文件存储体系
上面有关键两个属性,都有name 与 path,下面展示两个例子
实例一:
路径配置:
<paths>
<external-path name="my_images" path="Pictures" />
</paths>
使用:
File file = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),test.jpeg),;
Uri contentUri = FileProvider.getUriForFile(Main2Activity.this,"与清单文件中配置的android:authorities一样",file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); //可以使用Uri了
前面提到的path = Pictures ,所以这里使用的参数是Environment.DIRECTORY_PICTURES
前面如果是DCIM,这里相应的也要变成这个Environment.DIRECTORY_DCIM
实例二:
路径配置
<files-path name= "suiyidemingzi" path="apk_path" />
使用
private File apkpath = null;
private File contentFile = null;
apkpath = new File(getFilesDir(),"apk_path"); //先建立与路径配置中你自定义的path一致的子目录
,实例一通过传参的方式得到的(因为上述路径是系统肯定自带的)
contentFile = new File(apkpath,"test.jepg"); //这里文件名无所谓了,随便配置了
Uri contentUri = FileProvider.getUriForFile(Main2Activity.this, "和清单文件中配置的android:authorities一样",contentFile );
这时候就得到7.0以上版本使用的Uri了
发现name可以随便取,但是不能重复了,而path则要慎重用,和文件存储中的内部存储,外部存储一一相关,作用是为了抽象Uri中路径,不让外部应用直接得到使用.
上面的话如果难懂,下面是我自己的理解:
在xml中定义的标签:
external-path 就等于 代码中调用 Environment.getExternalStoragePublicDirectory这个方法得到的路径,path就是子路径
path表示的就是具体的哪个路径如Pictures文件夹
两者的作用一个样,但是要对应起来,这样系统在对外使用uri的时候才不为空。
files-path 就等于 getFilesDir()得到的路径。
如果上面的话很难理解,不妨可以这样想:
files-path + path 拼接成的路径 必须保证和你代码中构建的file路径一致。
(4)
使用:在7.0之前的版本还是使用Uri.fromFile();方法的得到Uri
Uri uriP;
uriP = FileProvider.getUriForFile(MineStudentFragment.this.getContext()
, "包名.建议是自定义的provider"
, file);
/**这里的参数二和清单文件注册时候的android:authorities一致*/
(5)
权限,还是需要的。如果是想像外部存储空间中存储
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
错误:java.lang.IllegalArgumentException: Failed to find configured root that contains xxx文件路径
再后来的使用中有些手机会返回这个异常,经过查找资料,发现需要额外配置如下:
<root-path
name = "root_path"
path="."
/>
这样就可以解决部分机型因为使用外置sd卡或者应用分身导致的问题。
华为 p9与华为 nove 2s在使用微信应用分身时候,会导致这个问题。
所以没事就配置上就可以!!!!