欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException

程序员文章站 2024-02-13 19:42:04
...

在旧版本中,我们经常使用”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>

完美解决了。

相关标签: android 适配