兼容Android N 调用相册和相机获取图片及Crash- FileUriExposedException解决方案

原因

Android N 文件系统权限发生了变化 ,为了提高私有文件的安全性,在targetSdk版本为N或者以后版本的app中,其私有目录将会限制访问。这可以防止私有文件元数据的泄露,比如文件大小或者是文件是否存在。但这给开发者带来了很多不利的影响:

文件的owner不能放宽文件权限,如果你使用MODE_WORLD_READABLE或者 MODE_WORLD_WRITEABLE操作文件,将会触发SecurityException。

当你跨package域传递file://的URI时,接收者得到的将是一个无权访问的路径,因此,这将会触发FileUriExposedException。


解决方案

对于这类操作,官方推荐的方式是使用FileProvider,当然你也可以使用ContentProvider。


源代码


    public void camera() {
        String savePath = Environment.getExternalStorageDirectory();
        String tempFileName = "temp.jpg"

        Intent intent = null;
        // 判断存储卡是否可以用,可用进行存储

        if (StorageUtils.hasSdcard()) {
            //设定拍照存放到自己指定的目录,可以先建好
            File file = new File(savePath);
            if(!file.exists()){
                file.mkdirs();
            }
            Uri pictureUri;
            File pictureFile = new File(savePath, tempFileName);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                ContentValues contentValues = new ContentValues(1);
                contentValues.put(MediaStore.Images.Media.DATA, pictureFile.getAbsolutePath());
                pictureUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
            }else {
                intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                pictureUri = Uri.fromFile(pictureFile);
            }
            if (intent != null) {
                intent.putExtra(MediaStore.EXTRA_OUTPUT,
                        pictureUri);
                startActivityForResult(intent, PHOTO_REQUEST_CAMERA);
            }
        }

    }

    /**
     * 从相册获取
     */
    public void gallery() {
        // 激活系统图库,选择一张图片
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType("image/*");
        startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
    }






    /**
     *选择图片返回结果处理
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (resultCode == RESULT_OK) {
            if (requestCode == PHOTO_REQUEST_GALLERY) {//相册
                if (data != null) {
                    // 得到图片的全路径
                    Uri uri = data.getData();
                    if (uri != null) {
                        tempPhotoPath = savePath + "/" + tempFileName;
                       //do something
                    }else {
                         Log.e("jaygoo","获取图片失败!");
                    }

                }

            } else if (requestCode == PHOTO_REQUEST_CAMERA) {//照相
            /*
                1.好些设备在调用系统相机照相后返回的 data为null
                  解决办法就是在调用相机之前先设定好照片的路径和名称,拍完后直接拿来用
                2.如果图片地址没有图片存在,则表示没有进行照相
            */

                tempPhotoPath = savePath + "/" + tempFileName;
                File file = new File(tempPhotoPath);
                if (file != null && file.exists()) {
                    Uri uri = Uri.fromFile(file);
                    try {
                        StorageUtils.saveFile(file, IMAGE_DIR);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                   //do something
                }else {
                    DialogToast.getInstance().showErrorToast("获取图片失败!");
                }

            }

        } else if (resultCode == UCrop.RESULT_ERROR) {
             Log.e("jaygoo","获取图片失败!");
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

扩展

这里兼容我采用的是ContentProvider,当然也可以使用Google官方推荐的FileProvider,这里我给出地址Demo,有兴趣的可以看下。FileProvider Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值