android 7.0拍照

参考:android 7.0拍照问题file:///storage/emulated/0/photo.jpeg exposed beyond app through ClipData.Item.getUri_飞渡浮舟-CSDN博客

 


Android7.0调用相机时出现新的错误:

android.os.FileUriExposedException: file:///storage/emulated/0/photo.jpeg exposed beyond app through ClipData.Item.getUri()

Paste_Image.png


解决办法:

在Application的onCreat()方法中添加以下代码:

    // android 7.0系统解决拍照的问题
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure(); 

Android 7.0 之拍照与图片裁剪适配

Android 7.0以后,用了Content Uri 替换了原本的File Uri,故在targetSdkVersion=24的时候,部分 “`Uri.fromFile()“` 方法就不适用了。 **File Uri 与 Content Uri 的区别** - File Uri 对应的是文件本身的存储路径 - Content Uri 对应的是文件在Content Provider的路径 所以在android 7.0 以上,我们就需要将File Uri转换为 Content Uri。具体转换方法如下:

package com.example.mvpfour.activity;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import com.example.mvpfour.BuildConfig;
import com.example.mvpfour.R;
import com.example.mvpfour.utils.LogUtils;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * Created by agen on 2018/7/4.
 */

public class CameraActivity extends AppCompatActivity implements View.OnClickListener {
    private static final int REQUEST_CODE_TAKE_PHOTO = 100;
    private static final int IMAGE_CROP_CODE = 101;
    private Button bt_take_photo;
    private String mCurrentPhotoPath;
    private ImageView iv_take_photo;
    private String cropImagePath;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera_activity);
        initView();
    }

    private void initView() {
        bt_take_photo = (Button) findViewById(R.id.bt_take_photo);

        bt_take_photo.setOnClickListener(this);
        iv_take_photo = (ImageView) findViewById(R.id.iv_take_photo);
        //        iv_take_photo.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_take_photo:
                takeSystemPhoto(this);
                break;
        }
    }

    private void takeSystemPhoto(Context context) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA)
                    .format(new Date()) + ".png";
            File file = new File(Environment.getExternalStorageDirectory(), filename);
            mCurrentPhotoPath = file.getAbsolutePath();

            Uri fileUri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                LogUtils.d("安卓7的拍照");
               /* fileUri = getUriForFile(context,
                        context.getPackageName() + ".fileprovider", file);*/
                fileUri = getImageContentUri(file);
                //                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file)); //Uri.fromFile(file)

            } else {
                fileUri = Uri.fromFile(file);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
            }
            //            fileUri = Uri.fromFile(file);
            LogUtils.d("fileUrl=" + fileUri);
            //            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
            startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PHOTO);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_TAKE_PHOTO) {
            iv_take_photo.setImageBitmap(BitmapFactory.decodeFile(mCurrentPhotoPath));
            //图片裁剪
            crop(mCurrentPhotoPath);
        } else if (resultCode == RESULT_OK && requestCode == IMAGE_CROP_CODE) {
            iv_take_photo.setImageBitmap(BitmapFactory.decodeFile(cropImagePath));
            //图片裁剪

        }
        // else tip?

    }

    /**
     * 转换 content:// uri
     *
     * @param imageFile
     * @return
     */
    public Uri getImageContentUri(File imageFile) {
        String filePath = imageFile.getAbsolutePath();
        Cursor cursor = getContentResolver().query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[]{MediaStore.Images.Media._ID},
                MediaStore.Images.Media.DATA + "=? ",
                new String[]{filePath}, null);

        if (cursor != null && cursor.moveToFirst()) {
            int id = cursor.getInt(cursor
                    .getColumnIndex(MediaStore.MediaColumns._ID));
            Uri baseUri = Uri.parse("content://media/external/images/media");
            return Uri.withAppendedPath(baseUri, "" + id);
        } else {
            if (imageFile.exists()) {
                ContentValues values = new ContentValues();
                values.put(MediaStore.Images.Media.DATA, filePath);
                return getContentResolver().insert(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            } else {
                return null;
            }
        }
    }

    /**
     * 裁剪
     * @param imagePath
     */
    private void crop(String imagePath) {
        String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA)
                .format(new Date()) + ".png";
        File file = new File(Environment.getExternalStorageDirectory(), filename);
        //        File file = new File("xxx.jpg");
         cropImagePath = file.getAbsolutePath();

        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(getImageContentUri(new File(imagePath)), "image/*");
        intent.putExtra("crop", "true");


        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        //        intent.putExtra("outputX", config.outputX);
        //        intent.putExtra("outputY", config.outputY);
        intent.putExtra("scale", true);
        intent.putExtra("return-data", false);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);
        startActivityForResult(intent, IMAGE_CROP_CODE);
    }
}

这是因为拍照存储的文件,也需要以Content Uri的形式,故采用以下办法解决:

Step.1 修改AndroidManifest.xml

<application
    ...>

    <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_paths"/>
    </provider>
</application>

Step.2 在res/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>

参考:Android 7.0 之拍照与图片裁剪适配_LeBron_Six的专栏-CSDN博客

相关步骤的解析和说明:

参考:安卓调用系统相机拍照、从相册选择图片并裁剪保存到SD卡(适配7.0) - 简书

参考Android拍照及图片裁剪、调用系统相册(兼容6.0以上权限处理及7.0以上文件管理)

Android拍照及图片裁剪、调用系统相册(兼容6.0以上权限处理及7.0以上文件管理) – Android开发中文站。。。

系统拍照和打开相册

 private static final int SYSTEM_PHOTO_REQUST = 1;//系统拍照的请求码
    private static final int REQUEST_PICK_IMAGE = 2;//系统相册的请求码
/**
     * 开启系统拍照,获取图片
     */
    private void takeSystemPhoto() {
        LogUtils.d("开启系统拍照_takeSystemPhoto");
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(intent, SYSTEM_PHOTO_REQUST);
    }

    //从相册选择
    private void getImage() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT).setType("image/*"),
                    REQUEST_PICK_IMAGE);
        } else {
            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");
            startActivityForResult(intent, REQUEST_PICK_IMAGE);
        }
    }

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        LogUtils.d("onActivityResult");
        if (data != null && resultCode == Activity.RESULT_OK) {
            rlPhoto.setVisibility(View.GONE);
            //系统拍照
            if (SYSTEM_PHOTO_REQUST == requestCode) {
                //加载本地缓存图片
                LogUtils.d("系统拍照返回的数据");
                Bundle bundle = data.getExtras();
                Bitmap bitmap = (Bitmap) bundle.get("data");
               
            } else if (REQUEST_PICK_IMAGE == requestCode) {
                //从相册选择
                if (data != null) {
                    realPathFromUri = RealPathFromUriUtils.getRealPathFromUri(this, data.getData());
                    LogUtils.d("realPathFromUri=" + realPathFromUri);
                  
                } else {
                    Toast.makeText(this, "图片损坏,请重新选择", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }

获取图片uri工具类

package com.example.mvpfour.utils;

/**
 * Created by agen on 2019/5/7.
 */

import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class RealPathFromUriUtils {
    /**
     * 根据Uri获取图片的绝对路径
     *
     * @param context 上下文对象
     * @param uri     图片的Uri
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */
    public static String getRealPathFromUri(Context context, Uri uri) {
        int sdkVersion = Build.VERSION.SDK_INT;
        if (sdkVersion >= 19) { // api >= 19
            return getRealPathFromUriAboveApi19(context, uri);
        } else { // api < 19
            return getRealPathFromUriBelowAPI19(context, uri);
        }
    }

    /**
     * 适配api19以下(不包括api19),根据uri获取图片的绝对路径
     *
     * @param context 上下文对象
     * @param uri     图片的Uri
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */
    private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) {
        return getDataColumn(context, uri, null, null);
    }

    /**
     * 适配api19及以上,根据uri获取图片的绝对路径
     *
     * @param context 上下文对象
     * @param uri     图片的Uri
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */
    @SuppressLint("NewApi")
    private static String getRealPathFromUriAboveApi19(Context context, Uri uri) {
        String filePath = null;
        if (DocumentsContract.isDocumentUri(context, uri)) {
            // 如果是document类型的 uri, 则通过document id来进行处理
            String documentId = DocumentsContract.getDocumentId(uri);
            if (isMediaDocument(uri)) { // MediaProvider
                // 使用':'分割
                String id = documentId.split(":")[1];

                String selection = MediaStore.Images.Media._ID + "=?";
                String[] selectionArgs = {id};
                filePath = getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs);
            } else if (isDownloadsDocument(uri)) { // DownloadsProvider
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId));
                filePath = getDataColumn(context, contentUri, null, null);
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            // 如果是 content 类型的 Uri
            filePath = getDataColumn(context, uri, null, null);
        } else if ("file".equals(uri.getScheme())) {
            // 如果是 file 类型的 Uri,直接获取图片对应的路径
            filePath = uri.getPath();
        }
        return filePath;
    }

    /**
     * 获取数据库表中的 _data 列,即返回Uri对应的文件路径
     *
     * @return
     */
    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        String path = null;

        String[] projection = new String[]{MediaStore.Images.Media.DATA};
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                int columnIndex = cursor.getColumnIndexOrThrow(projection[0]);
                path = cursor.getString(columnIndex);
            }
        } catch (Exception e) {
            if (cursor != null) {
                cursor.close();
            }
        }
        return path;
    }

    /**
     * @param uri the Uri to check
     * @return Whether the Uri authority is MediaProvider
     */
    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri the Uri to check
     * @return Whether the Uri authority is DownloadsProvider
     */
    private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值