Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException
在旧版本中,我们经常使用”file:///”绝对路径来传递文件地址的方式,但是在Android 7.0后在接收方访问时很容易触发FileUriExposedException的异常。
Google官方推荐我们使用FileProvider来生成一个content://格式的URI来解决。
一、 在manifest配置文件中声明一个provider
<manifes xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp">
<application
...>
<provider
//指向v4包里的FileProvider类
android:name="android.support.v4.content.FileProvider"
//对应你的content uri的基础域名,生成的uri将以content://com.example.myapp.fileprovider作为开头
android:authorities="包名.fileprovider"
//设置允许获取访问文件的临时权限
android:grantUriPermissions="true"
//设置不允许导出,我们的FileProvider应该是私有的
android:exported="false"
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
//用于设置FileProvider的文件访问路径
android:resource="@xml/filepaths"
/>
</provider>
...
</application>
</manifest>
二、在res目录下的xml文件夹里创建一个 filepaths.xml 文件,具体要起什么名字就看你项目需求了
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>
在这里,有几点属性要说明
1.path子元素则用来定义content uri所对应的路径目录。
2.name属性:指明了FileProvider在content uri中需要添加的部分,这里的name为my_images,所以对应的content uri为
content://com.example.myapp.fileprovider/my_images
3.path属性:标签对应的路径地址为Context.getFilesDir()]()返回的路径地址,而path属性的值则是该路径的子路径,这里的path值为”images/”,那组合起来的路径如下所示:
Content.getFilesDir() + "/images/"
content://com.example.myapp.fileprovider/my_images/xxx.jpg
就会找到这里配置的path路径
Content.getFilesDir() + "/images/"
并查找xxx.jpg文件
对应路径的配置,官方文档列出了如下配置项:
<files-path name="*name*" path="*path*" />
对应 Context.getFilesDir() 的路径地址
<cache-path name="*name*" path="*path*" />
对应 getCacheDir() 获取的路径
<external-path name="*name*" path="*path*" />
对应 Environment.getExternalStorageDirectory()的路径地址
<external-files-path name="*name*" path="*path*" />
对应Context#getExternalFilesDir(String) Context.getExternalFilesDir(null) 返回的路径地址
<external-cache-path name="*name*" path="*path*" />
以此类推,我们可以根据自己所需共享的目录来配置不同的path路径.
download下来的文件存放的位置应与filepaths.xml中一致
File f = new File(Environment.getExternalStorageDirectory() + NounBean.DOWNLOAD_APP_DIR + “/hhh” + bean.getVersion() + “.apk”);
四、在你的安装APP的方法中,
//安装APK程序
public static void installAPK(Context context, File file) {
CLog.e("OpenFile", file.getName());
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
//判断当前手机的版本,如果大于7.0就用FileProVider方法,否则用以前的方法,注意要给个权限
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M){
intent.setDataAndType(FileProvider.getUriForFile(context,"com.smartmob.lucktry.file_provider",file),
"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
context.startActivity(intent);
}
**但是还会报错:
Failed to find configured root that contains**
原来我选择了存储位置在外置SD卡上,因此APP的存放地址实际上是在外置sd卡上的。
然后我改了一下 filepaths.xml 的一些内容,变成了
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="lucktry/app" path="" />
</paths>
完美解决了。
上一篇: HTML页面的哈希(hash)路由原理+原生js案例
下一篇: 分享:nuxt路由