Android 7.0拍照及剪裁

在android7.0以上系统,如果uri仍然使用fill类型Uri的话,拍照和剪裁时可能会出现以下两种错误:

//错误一
android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.yuyh.imgsel/cache/1486438962645.jpg exposed beyond app through ClipData.Item.getUri()
//错误二
android.os.FileUriExposedException: file:///storage/emulated/0/DCIM/RxGalleryFinal/IMG_20161018180127.jpg exposed beyond app through Intent.getData()

之所以会出现以上两种错误是因为我们7.0之前的进行拍照或者裁剪时传递Uri时可以直接使用Uri.FromFile(file)的形式进行Uri的传递,而从7.0之后我们的Uri不允许通过这种方式传递,而可以通过FileProvider.getUriFromFile进行Uri的转换即可。具体实现方式如下:

 android7.0以前拍照代码
   /**
     * 拍照
     */
    public void takePhoto() {

        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
                     outputPath =Environment.getExternalStorageDirectory().getAbsolutePath().concat(File.separator).concat("facePhoto.jpg");
        }else {
            outputPath =getCacheDir().getAbsolutePath().concat(File.separator).concat("facePhoto.jpg");
        }
        try {
            Intent intent = new Intent();
            intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
            Uri uri=Uri.fromFile(new File(outputPath));
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            startActivityForResult(intent,REQUEST_CODE_TAC_PIC);
        } catch (ActivityNotFoundException e) {
            ToastHelp.showShortToast(getString(R.string.errormsg_cantopencamera));
            return;
        }
    }
android 7.0之前裁剪代码:
private void cropPicture(Uri  sourceUri){
        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            tempFile =new File(getExternalCacheDir(),"crop.jpg");
        }else {
            tempFile =new File(getCacheDir()+"crop.jpg");
        }
        if(!tempFile.exists()){
            try {
                tempFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(sourceUri, "image/*");
        intent.putExtra("crop", "true");
        // aspectX   aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", width);
        intent.putExtra("outputY", width);
        intent.putExtra("return-data", true);// 是否返回数据
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);// 关闭人脸检测
        startActivityForResult(intent, REQUEST_CODE_CUT_PIC);
    }

之所以以上代码放到7.0以上系统会报以上两种类型错误,是因为7.0以后不允许向外部应用传递file类型Uri,并且7.0以后私有文件访问也受到限制;具体原因不做过多解释,如果有需要可以查看7.0适配相关文档,文档连接我会在当前文档最后提供给大家。具体7.0以上解决方案如下:

1.在清单文件中添加

    <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>

2.在res创建一个xml文件夹,res/xml/provider_paths.xml
具体provider_paths.xml内容如下

    <?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path="."/>
</paths>

3.代码更改适配7.0

7.0以上系统拍照代码
   /**
     * 拍照
     */
    public void takePhoto() {

        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
                     outputPath =Environment.getExternalStorageDirectory().getAbsolutePath().concat(File.separator).concat("facePhoto.jpg");
        }else {
            outputPath =getCacheDir().getAbsolutePath().concat(File.separator).concat("facePhoto.jpg");
        }
        try {
            Intent intent = new Intent();
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//此处新加代码不可省略
            intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
            Uri uri=getUriForFile(AccountInfoActivity.this,new File(outputPath));//此处为更改Uri为content类型的Uri
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            startActivityForResult(intent,REQUEST_CODE_TAC_PIC);
        } catch (ActivityNotFoundException e) {
            ToastHelp.showShortToast(getString(R.string.errormsg_cantopencamera));
            return;
        }
    }
  7.0以上裁剪图片代码
 private void cropPicture(Uri  sourceUri){
        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            tempFile =new File(getExternalCacheDir(),"crop.jpg");
        }else {
            tempFile =new File(getCacheDir()+"crop.jpg");
        }
        if(!tempFile.exists()){
            try {
                tempFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//此处新加代码不可省略
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);//此处新加代码不可省略
        intent.setDataAndType(sourceUri, "image/*");//数据源uri对于拍照返回携带的uri需要进行处理  要在传递之前先转换成content类型的uri
        intent.putExtra("crop", "true");
        // aspectX   aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", width);
        intent.putExtra("outputY", width);
        intent.putExtra("return-data", true);// 是否返回数据
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));//此时数据时设置给当前app内部的uri无需进行转换  //此处有些文档也对Uri进行转换 但经自己亲测后转换反而会有问题
        intent.putExtra("outputFormat",Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);// 关闭人脸检测
        startActivityForResult(intent, REQUEST_CODE_CUT_PIC);
    }

此时剪裁代码有传进来一个Uri,如果是拍照之后的剪裁那么在剪裁之前需要将file:Uri转换成content://Uri及假如是拍照之后的剪裁我们代码如下:

 Uri uri=getUriForFile(AccountInfoActivity.this,new File(outputPath));//拍照之后的剪裁 先对Uri进行转换 再调用剪裁的方法
 cropPicture(uri);

转化Uri通用方法

 /**
     * 征对不同版本对uri的处理
     * @param context
     * @param file
     * @return
     */
    private  Uri getUriForFile(Context context, File file) {
        if (context == null || file == null) {
            throw new NullPointerException();
        }
        Uri uri;
        if (Build.VERSION.SDK_INT >= 24) {
            uri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID+".fileprovider", file);
        } else {
            uri = Uri.fromFile(file);
        }
        return uri;
    }

正对7.0以后的拍照、剪裁除了以上方案外还有史上最简单的方案,几句代码即可搞定,简单暴力
在Application的onCreat()方法中添加以下代码:

 StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure(); 

相关参考文档:7.0适配链接

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值