Android7.0 以上版本 使用 Uri.fromFile(File) 报错

我在学习Android中Camera的时候发现,如果我们想要原图,直接读取图片位置显示的时候程序直接崩溃了,我发现报了下面这个错误:

android.os.FileUriExposedException: file:///storage/emulated/0/temp.png exposed beyond app through ClipData.Item.getUri() 
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1958) 
at android.net.Uri.checkFileUriExposed(Uri.java:2348) 
at android.content.ClipData.prepareToLeaveProcess(ClipData.java:941) 

然后我在百度上发现是Android7.0更新对于Uri的判断,不再允许在app中把file://Uri暴露给其他app,包括但不局限于通过Intent或ClipData 等方法。 
原因在于使用file://Uri会有一些风险,比如:

  • 文件是私有的,接收file://Uri的app无法访问该文件。
  • 在Android6.0之后引入运行时权限,如果接收file://Uri的app没有申请READ_EXTERNAL_STORAGE权限,在读取文件时会引发崩溃。

因此,google提供了FileProvider,使用它可以生成content://Uri来替代file://Uri。 
接下来我们来说在代码中我们怎么进行操作吧:

第一步:在AndroidManifest.xml中加上摄像头、读写磁盘的权限:

 

<uses-permission android:name="android.permission.CAMERA" />   
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

第二步:在AndroidManifest.xml中加上自定义权限的ContentProvider: 

<provider  
    android:name="android.support.v4.content.FileProvider"   
    android:authorities="你的包名.provider"  
    android:exported="false"  
    android:grantUriPermissions="true">  
    <meta-data  
        android:name="android.support.FILE_PROVIDER_PATHS"  
        android:resource="@xml/自定义名字" />  
</provider>  

我们来解释一下里面的各个属性吧:

  1. android:name="android.support.v4.content.FileProvider"provider你可以使用v4包提供的FileProvider,或者自定义您自己的,只需要在name申明就好了,一般使用系统的就足够了。
  2. android:authorities="你的包名.FileProvider"自定义的权限,类似命名空间
  3. android:exported="false"是否设置为独立进程
  4. android:grantUriPermissions="true"是否拥有共享文件的临时权限
  5. android:resource="@xml/自定义名字"共享文件的文件根目录,名字可以自定义

第三步:在项目res目录下创建一个xml文件夹,里面创建一个和上面自定义名字一样的.xml文件: 

 

<?xml version="1.0" encoding="utf-8"?>  
<paths>  
    <external-path  
        name="自定义名字"  
        path="" />  
</paths>  

解释一下属性:

  1. name="自定义名字" 一个引用字符串
  2. path="."文件夹“相对路径”,完整路径取决于当前的标签类型。(path可以为空,表示指定目录下的所有文件、文件夹都可以被共享)

第四步:在代码中将Uri.fromFile替换: 

if (Build.VERSION.SDK_INT >= 24) {
    uri = FileProvider.getUriForFile(this,"com.gin.xjh.camera.provider", new File(mFilePath));
} else {
    uri = Uri.fromFile(new File(mFilePath));
}

 

这样就可以代替Uri.fromFile(File)的作用了。 
但是我们要注意以下的几个问题:

  1. provider需要保证唯一性,即在不同的app里面需要使用不同的名称(建议:项目名+provider)
  2. 如果两个项目使用了同一个provider,真机上无法装载
  3. 注意代码里面的provider名字需要和清单的provider名字一致,否则会报空

另外一种解决方法 

@Override
protected void onCreate(Bundle savedInstanceState) {}

方法中添加调用

 // android 7.0以上系统解决拍照的问题
  private void initPhotoError(){
    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());
    builder.detectFileUriExposure();
  }

使用方法:

Uri.fromFile(new File(path) 

// 访问图片库
    //Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    //startActivityForResult(i, RESULT_LOAD_IMAGE);

    // 访问相机
    Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //设置拍照之后的存储路径
    savePicPath = getExternalFilesDir("/cameraFile").getPath()+ File.separator + new Date().getTime() + ".jpg";
    // 保存
    i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(savePicPath)));
    //由于我们需要调用完Camera后,可以返回Camera获取到的图片,
    //所以,我们使用startActivityForResult来启动Camera
    startActivityForResult(i, RESULT_LOAD_IMAGE);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值