Android 11下Camera Action CROP图片裁剪功能无法启动问题
问题:上传图片时照片裁剪流程无法启动
现象
尝试上传图片时,照片裁剪流程无法启动。
Logcat错误日志
[main] - src or des missed, src: content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FPictures%2FScreenshots%2FScreenshot_2022-11-13-19-56-04-778_org.hcg.IF.jpg, des: null
相关代码
启动裁剪的方法
public static final String IMAGE_UNSPECIFIED = "image/*"; public void startPhotoZoom(Uri uri, int resValue) { System.out.println(uri); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 90); intent.putExtra("outputY", 90); intent.putExtra("return-data", true); try { startActivityForResult(intent, resValue); } catch(Throwable thw){ //ActivityNotFoundException } }
Activity回调代码
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); try { // photo taker onPhotoActivityResult(requestCode, resultCode, data); } catch( Exception e ) { } } protected void onPhotoActivityResult(int requestCode, int resultCode, Intent data) { // Log.d("onPhotoActivityResult", "resultCode: "+resultCode+",requestCode:"+requestCode); if (resultCode == RESULT_OK) { switch (requestCode) { case Cocos2dxHandler.TAKE_PICTURE: // 设置文件保存路径这里放在跟目录下 File picture = new File(Environment.getExternalStorageDirectory() + "/image.jpg"); startPhotoZoom(Uri.fromFile(picture), Cocos2dxHandler.CLIP_TAKEN_PICTURE); break; case Cocos2dxHandler.CHOOSE_PICTURE: if(data != null){ //照片的原始资源地址 Uri originalUri = data.getData(); Log.d("onPhotoActivityResulted", "Uri: "+originalUri); if(originalUri != null){ startPhotoZoom(originalUri, Cocos2dxHandler.CLIP_CHOOSED_PICTURE); } } break; case Cocos2dxHandler.CLIP_TAKEN_PICTURE: case Cocos2dxHandler.CLIP_CHOOSED_PICTURE: if(data != null) { saveImg(data); } break; case Cocos2dxHandler.CHOOSE_VIDEO: if(data != null) { Log.d("xiaomi", "select success: "); saveVideo(data); } break; default: break; } } }
解决方案
报错des: null是因为MIUI系统的裁剪实现强制要求指定输出目标Uri,你的代码只设置了return-data,没有提供输出文件路径,导致裁剪流程启动失败。
1. 修改启动裁剪的方法
public static final String IMAGE_UNSPECIFIED = "image/*"; public void startPhotoZoom(Uri uri, int resValue) { System.out.println(uri); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 90); intent.putExtra("outputY", 90); // 取消return-data,改用输出Uri方式(避免大图OOM,同时适配MIUI) intent.putExtra("return-data", false); // 创建临时输出文件(存在应用缓存目录,无需额外权限) File outputFile = new File(getExternalCacheDir(), "cropped_" + System.currentTimeMillis() + ".jpg"); // 通过FileProvider生成安全Uri(适配Android 7.0+) Uri outputUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", outputFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); // 授予裁剪应用临时读写权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); try { startActivityForResult(intent, resValue); } catch(Throwable thw){ thw.printStackTrace(); // 建议打印异常,方便后续排查 } }
2. 调整裁剪回调逻辑
原来的return-data不再生效,需要直接读取临时输出文件:
case Cocos2dxHandler.CLIP_TAKEN_PICTURE: case Cocos2dxHandler.CLIP_CHOOSED_PICTURE: // 这里需要注意:如果要准确对应输出文件,建议把outputFile的路径保存为成员变量 // 下面是简化写法,实际可根据业务调整 File croppedDir = getExternalCacheDir(); if(croppedDir != null){ // 遍历缓存目录找最新的裁剪文件,或者提前保存文件路径 File[] files = croppedDir.listFiles((dir, name) -> name.startsWith("cropped_")); if(files != null && files.length > 0){ // 取最新的文件 File croppedFile = files[files.length-1]; saveImgFromFile(croppedFile); // 修改saveImg方法,支持从File读取图片 } } break;
3. 配置FileProvider(Android 7.0+要求)
- 在
AndroidManifest.xml中添加Provider配置:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
- 在
res/xml目录下创建file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 允许访问应用外部缓存目录 --> <external-cache-path name="external_cache" path="."/> </paths>
补充说明
- 不同厂商对系统裁剪的实现差异很大,MIUI强制要求指定输出目标,而部分原生系统允许只使用
return-data。 - 用
return-data返回Bitmap容易触发OOM,尤其是大图场景,使用输出Uri的方式更稳定通用。
内容的提问来源于stack exchange,提问作者Maksim Lebid
相关产品推荐
相关产品推荐

