Android 7.0处理系统裁剪功能异常(适配版)

Android系统裁剪功能


实现图片裁剪,通过Intent开启系统裁剪功能。这里是一个常见裁剪配置,包含裁剪的尺寸,原始图片地址,裁剪后的图片地址等配置。具体代码如下:

    /**
     * 根据Uri裁剪图片
     *
     * @param uri
     */
    private void crop(Uri uri) {
        // 裁剪图片意图
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        // 裁剪框的比例,1:1
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // 裁剪后输出图片的尺寸大小
        intent.putExtra("outputX", 250);
        intent.putExtra("outputY", 250);
        // 图片格式
        intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
        // 取消人脸识别
        intent.putExtra("noFaceDetection", true);
       /* //是否把剪切后的图片通过data返回
        intent.putExtra("return-data", true);*/
        File cropFile = BitmapUtils.getDiskFile(getApplicationContext(), BitmapUtils.getBitmapFileName());
        currentPicturePath = cropFile.getAbsolutePath();
        Uri cropUri = Uri.fromFile(cropFile);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);
        // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT
        startActivityForResult(intent, PHOTO_REQUEST_CUT);
    }

Android 6.0的权限问题,请阅读Android6.0权限处理篇 Android EasyPermissions官方库,高效处理权限
如何配置,这里省略不说。

在Android 7.0模拟器上,运行以上的代码,发生以下异常:

 java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=2, result=-1, data=Intent {  }}  
    to activity {com.zhongke.weiduo/com.zhongke.weiduo.mvp.ui.activity.ActivateDeviceActivity}:

           android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.zhongke.weiduo/files/Pictures/3cd82f9a4c2fe0caa21fe32e339c1161.png  
                        exposed beyond app through Intent.getData() 

               at android.app.ActivityThread.deliverResults(ActivityThread.java:4053)
               at android.app.ActivityThread.handleSendResult(ActivityThread.java:4096)
               at android.app.ActivityThread.-wrap20(ActivityThread.java)

处理FileUriExposedException,需要在项目中自定义配置FileProvider,接下来会介绍到。

项目中配置FileProvider


相关配置,请阅读Android 7.0 处理android.os.FileUriExposedException异常

按照以上介绍,实现FileProvider配置后,修改以上的代码如下:

 /**
     * 根据Uri裁剪图片
     *
     * @param uri
     */
    private void crop(Uri uri) {
        // 裁剪图片意图
        Intent intent = new Intent("com.android.camera.action.CROP");
       if (Build.VERSION.SDK_INT>=24){
            Uri newUri=FileProvider.getUriForFile( getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider",new File(uri.getEncodedPath()));
            intent.setDataAndType(newUri ,"image/*");
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }else {
            intent.setDataAndType(uri, "image/*");
        }
        intent.putExtra("crop", "true");
        // 裁剪框的比例,11
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // 裁剪后输出图片的尺寸大小
        intent.putExtra("outputX", 250);
        intent.putExtra("outputY", 250);
        // 图片格式
        intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
        // 取消人脸识别
        intent.putExtra("noFaceDetection", true);
       /* //是否把剪切后的图片通过data返回
        intent.putExtra("return-data", true);*/
        File cropFile = BitmapUtils.getDiskFile(getApplicationContext(), BitmapUtils.getBitmapFileName());
        currentPicturePath = cropFile.getAbsolutePath();
       if (Build.VERSION.SDK_INT>=24){
            intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", new File(currentPicturePath)));
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }else{
            Uri cropUri = Uri.fromFile(cropFile);
            //设置裁剪后文件保存路径
            intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);
        }
        // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT
        startActivityForResult(intent, PHOTO_REQUEST_CUT);
    }

再次执行,纳尼,又发生异常了,简直泪奔了。还得老实去查看,具体如下:

DatabaseUtils: Writing exception to parcel  
        java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.zhongke.weiduo.provider/root/storage/emulated/0/Android/data/com.zhongke.weiduo/files/Pictures/20b11deb8c0de58d6b95acf60489a67d.png from pid=3426, uid=10049 requires the provider be exported, or grantUriPermission()
        at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:608)
        at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:483)
        at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:474)
        at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:419)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:313)
        at android.os.Binder.execTransact(Binder.java:565)

在代码中intent.setDataAndType(newUri ,"image/*");intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
已经授写入权限,而报错却提醒未授予读取权限。因此,根据提示,改为授予读取权限intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

修改完成后,再次执行,发觉以上bug呗完美填掉。小样,总算解决掉你了。这bug呀,雨后春笋,解决一个又一个出现了,这时的我,内牛满面。依旧打起精神去看bug , 具体如下:

Writing exception to parcel
            java.lang.SecurityException: Permission Denial: writing android.support.v4.content.FileProvider uri content://com.zhongke.weiduo.provider/root/storage/emulated/0/Android/data/com.zhongke.weiduo/files/Pictures/9348410c5f10dc07b76271c15ac65999.png from pid=27283, uid=10049 requires the provider be exported, or grantUriPermission()
            at android.content.ContentProvider.enforceWritePermissionInner(ContentProvider.java:682)
            at android.content.ContentProvider$Transport.enforceWritePermission(ContentProvider.java:497)
            at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:469)
            at android.content.ContentProvider$Transport.openAssetFile(ContentProvider.java:384)
            at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:262)
            at android.os.Binder.execTransact(Binder.java:565)

裁剪图片的Intent带有图片原始路径和存储路径,因此需要通过FileProvider处理两次URI。

脑袋中出现一个大大的问号,心里就纳闷了,为什么授予了读取权限和写入权限,还报错写入权限错误呢。

PS : 带着疑问,去上路,寻找与我一样内心受伤的小伙伴,交流一下感情,看是否可以互相安慰一下。

解决方式


去StackOverFlow搜索了file provider - permission denial一把,还真的,让我找到了相关答案。

有位小伙伴提出一个解释:

在Intent中添加Flag权限前,需要Context.grantUriPermission()

这里,编写一个工具类,具体代码如下:

public class FileProviderUtils {
    /**
     * 为Intent中文件路径的Uri,授予读写的权限
     * @param context
     * @param intent
     * @param uri
     */
    public static  void grantUriPermission(Context context , Intent intent,Uri uri){
        List<ResolveInfo> resolveInfoList=context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo:resolveInfoList){
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }
}

实现Android裁剪功能,完整代码,如下:

/**
     * 根据Uri裁剪图片
     *
     * @param uri
     */
    private void crop(Uri uri) {
        // 裁剪图片意图
        Intent intent = new Intent("com.android.camera.action.CROP");
       if (Build.VERSION.SDK_INT>=24){
           Log.i(tag,"裁剪前的图片路径:"+uri.getEncodedPath());
            Uri newUri=FileProvider.getUriForFile( getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider",new File(uri.getEncodedPath()));
            intent.setDataAndType(newUri ,"image/*"); 

           FileProviderUtils.grantUriPermission(this,intent,uri);
           intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }else {
            intent.setDataAndType(uri, "image/*");
        }
        intent.putExtra("crop", "true");
        // 裁剪框的比例,11
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // 裁剪后输出图片的尺寸大小
        intent.putExtra("outputX", 250);
        intent.putExtra("outputY", 250);
        // 图片格式
        intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
        // 取消人脸识别
        intent.putExtra("noFaceDetection", true);
        File cropFile = BitmapUtils.getDiskFile(getApplicationContext(), BitmapUtils.getBitmapFileName());
        currentPicturePath = cropFile.getAbsolutePath();
       if (Build.VERSION.SDK_INT>=24){
           Log.i(tag,"裁剪后的存储的图片路径:"+currentPicturePath);
           Uri saveFileUri= FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider",cropFile);
           intent.putExtra(MediaStore.EXTRA_OUTPUT,saveFileUri); 

            FileProviderUtils.grantUriPermission(this,intent,saveFileUri);
           intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }else{
           //设置裁剪后文件保存路径
            Uri cropUri = Uri.fromFile(cropFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);
        }
        // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT
        startActivityForResult(intent, PHOTO_REQUEST_CUT);
    }

裁剪成功后,onActivityResult()返回,裁剪成功标识,则加载图片的代码,由各位小伙伴自行解决。

注意点:以上代码适配Android7.0以上和7.0以下API,实现裁剪功能。

长征路漫漫,终于搞定了,效果如下所示

这里写图片描述


资源参考

猜你喜欢

转载自blog.csdn.net/hexingen/article/details/78747810