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>

完美解决了。

猜你喜欢

转载自blog.csdn.net/github_37271067/article/details/76676290