You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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+要求)

  1. 在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>
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:41:13