美文网首页
打开相机以及FileProvider适配

打开相机以及FileProvider适配

作者: 边走边玩FREE | 来源:发表于2019-06-06 17:05 被阅读0次

    在打开相机获取图片时,大家最常用的是ACTION_IMAGE_CAPTURE,因为这个方法比较简单,直接打开系统相机,并且不需要获取相机权限,就能得到对应的图片。但自己再使用时,还是遇到了问题,因此在做总结一下。

    文章目录:

    • ACTION_IMAGE_GAPTURE说明
    • 相机基本使用
    • Bitmap和Uri转换
    • Content Uri和File Uri相互转换(7.0适配)
    • 总结

    1. ACTION_IMAGE_GAPTURE基本使用

    先看看官方说明:


    image.png

    通过上面的说明,可以得出两点:

    1. 通过该action打开相机时,若设置了 EXTRA_OUTPUT参数,则可以获得全尺寸的图片;若没有设置EXTRA_OUTPUT参数,则可以在onActivityResult中intent字段附加参数中获得缩小尺寸的图片。
    2. 若targetVersion大于等于23(6.0),若在Manifest文件中声明了Camera权限,但没有自己申请,则会引发SecurityException异常。

    打开相机操作

    方式一:设置EXTRA_OUTPUT
    1. 指定文件,打开相机代码如下:(具体见注释)
        File outputImage = new File(getExternalCacheDir(), "test.jpg"); 
        try {
            if (outputImage.exists()) {
                outputImage.delete();
            }
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        imageUri = Uri.fromFile(outputImage); //根据文件获取uri ,7.0以前处理  imageUri为全局
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); //无需相机权限
        intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); //指定图片存放位置
        startActivityForResult(intent,1);
    
    1. 在onActivityResult获取相机拍摄的相片,此时 data为null,代码如下:
        protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
            switch (requestCode) {
                case 1:
                    if (resultCode == RESULT_OK) {
                        try {
                            //因为指定了文件位置,因此直接从指定uri中获取即可。
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                            //获取bitmap后,可以设置给imageView等
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    
    
    方式二:没有设置EXTRA_OUTPUT
    1. 打开相机代码如下:
     Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); //无需相机权限
     startActivityForResult(intent,1);
    

    2.在onActivityResult获取相机拍摄的相片,代码如下:

        protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
            switch (requestCode) {
                case 1:
                    if (resultCode == RESULT_OK) {
                        Bundle bundle = data.getExtras();
                        Bitmap bitmap = (Bitmap) bundle.get("data");
                        imageView.setImageBitmap(bitmap);
                    }
                    break;
                default:
                    break;
            }
        }
    

    Bitmap和Uri相互转换

    没有设置写入路径时,我们通过intent附加字段获取图片,有时需要通过Bitmap来获取图片的Uri做相关操作,就可以通过一下方式处理。

    • 通过Bitmap获取Uri,该Uri为 content:// 开始,并且需要文件系统读写权限
    Uri uri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, null,null));
    
    • 通过Uri 获取Bitmap ,该Uri为 content:// 开始,并且需要文件系统读写权限
    Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
    

    详细说明可以参考官方文档:<u>https://developer.android.google.cn/reference/kotlin/android/provider/MediaStore.Images.Media.html#insertimage</u>

    Content Uri和File Uri转换以及7.0适配

    android 7.0 以上,需要通过Content Uri来访问手机文件系统位置,并且可以为Uri设置临时目录访问权限,供其他应用访问。因此为了兼容性,打开相机代码得做一定得修改。

    1. 添加功能函数,转换Uri
        /**
         * 7.0适配,获取 不同环境下的 sd卡的文件uri
         * @param context
         * @param file
         * @return
         */
        public Uri getUri(Context context, File file) {
            Uri uri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {  //7.0 以上 通过FileProvider获取文件路径
                uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
            } else {
                uri = Uri.fromFile(file); //7.0 以下
            }
            return uri;
        }
    
    1. 然后修改打开相机代码:
        File outputImage = new File(getExternalCacheDir(), "test.jpg");
        try {
            if (outputImage.exists()) {
                outputImage.delete();
            }
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //imageUri = Uri.fromFile(outputImage); 7.0以下处理
        imageUri = getUri(MainActivity.this, outputImage); //兼容7.0
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); //无需相机权限
        intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
        startActivityForResult(intent,1);
    
    

    有了Content Uri 并不能完全满足我们的需求,有时需要获取文件的path来进行相关处理,因此需要把Content Uri转换成我们需要path。
    在7.0 适配时,通过 FileProvider.getUriForFile,将File转换成Content Uri,其实FileProvider 也封装了 getFileForUri函数,只是没有对我们提供,因此我们可以通过反射,拿到该函数,满足我们的需求。具体如下:

    //根据 传入的 Content Uri 来获取对应的 file path
     public String getPathFromURi(Uri uri) {
            String filePath = null;
    
            List<PackageInfo> packs = this.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS);
            if (packs != null) {
    
                String fileProviderClassName = FileProvider.class.getName();
                for (PackageInfo pack : packs) {
                    ProviderInfo[] providers = pack.providers;
                    if (providers != null) {
                        for (ProviderInfo provider : providers) {
                            if (uri.getAuthority().equals(provider.authority)) {
                                if (provider.name.equalsIgnoreCase(fileProviderClassName)) {
                                    Class<FileProvider> fileProviderClass = FileProvider.class;
                                    try {
                                        Method getPathStrategy = fileProviderClass.getDeclaredMethod("getPathStrategy", Context.class, String.class);
                                        getPathStrategy.setAccessible(true);
                                        Object invoke = getPathStrategy.invoke(null, this, uri.getAuthority());
                                        if (invoke != null) {
                                            String PathStrategyStringClass = FileProvider.class.getName() + "$PathStrategy";
                                            Class<?> PathStrategy = Class.forName(PathStrategyStringClass);
                                            Method getFileForUri = PathStrategy.getDeclaredMethod("getFileForUri", Uri.class);
                                            getFileForUri.setAccessible(true);
                                            Object invoke1 = getFileForUri.invoke(invoke, uri);
                                            if (invoke1 instanceof File) {
                                                filePath = ((File) invoke1).getAbsolutePath();
                                                return filePath;
                                            }
                                        }
                                    } catch (NoSuchMethodException e) {
                                        e.printStackTrace();
                                    } catch (InvocationTargetException e) {
                                        e.printStackTrace();
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    } catch (ClassNotFoundException e) {
                                        e.printStackTrace();
                                    }
    
                                    break;
                                }
    
                                break;
                            }
                        }
    
                    }
    
                }
            }
            return filePath;
        }
    

    以上只是提供了一种获取真实路径的方式,其他方式大家可以自己百度。

    总结

    1. 通过该action打开相机时,若设置了 EXTRA_OUTPUT参数,则可以获得全尺寸的图片,但onActivityResult 函数的Intent字段为null;
      若没有设置EXTRA_OUTPUT参数,则可以在onActivityResult中intent字段附加参数中获得缩小尺寸的图片
      (Bitmap)(intent.getExtras().getData("data"))。
    2. 若targetVersion大于等于23(6.0),若在Manifest文件中声明了Camera权限,但没有自己申请,则会引发SecurityException异常。若没有在Manifest中声明Camera权限,则无需申请权限。 但项目开发时,会引入其他的包,难保其他包里面声明了Camear权限,因此为了兼容性,建议大家手动申请权限。
    3. android 7.0适配,主要将file:///转换成content://,为了对应不同的需求,可以通过FileProvider提供的方法,进行相互转化。(getFileForUri方法需要听过反射获取)

    相关文章

      网友评论

          本文标题:打开相机以及FileProvider适配

          本文链接:https://www.haomeiwen.com/subject/fqduxctx.html