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

基于Camera API 2拍照存为Bitmap,实现跨Activity显示图片的技术问询

Android相机拍摄后跨Activity显示图像的解决方案

嘿,看你这段代码,应该是在处理相机ImageReader回调获取图像,然后想传到另一个Activity显示对吧?我帮你梳理下现有代码的问题,再给你一套可行的方案:

先说说你现有代码的几个坑

  • Image资源没释放:调用acquireLatestImage()拿到Image后,必须手动调用image.close(),不然相机资源会被占着,后续拍照可能直接崩或者无响应。
  • Bitmap转换可能失败:相机输出的Image一般是YUV_420_888格式,你直接把Plane的byte数组丢给BitmapFactory.decodeByteArray(),大概率解码出的是乱图,因为BitmapFactory不认YUV格式。
  • Bitmap传递方式有风险:你用自定义SetBitmap类传Bitmap,如果Bitmap太大(比如4K照片),直接通过内存传递很容易触发TransactionTooLargeException,或者导致内存溢出。

完整解决方案步骤

1. 正确处理Image资源 + 转Bitmap(YUV转Bitmap)

先把onImageAvailable方法改对,加上资源释放,再用正确的方式把YUV图像转成Bitmap:

@Override
public void onImageAvailable(ImageReader imageReader) {
    Image image = null;
    try {
        image = imageReader.acquireLatestImage();
        if (image == null) {
            return; // 有时候会拿到null,直接跳过
        }
        // 把YUV格式的Image转成Bitmap
        Bitmap shotBitmap = convertYuvImageToBitmap(image);
        
        // 接下来把Bitmap传到另一个Activity,推荐用「文件路径」的方式,避免内存问题
        String imagePath = saveBitmapToPrivateStorage(shotBitmap);
        jumpToDisplayActivity(imagePath);
        
        // 用完Bitmap及时回收,减少内存占用
        if (!shotBitmap.isRecycled()) {
            shotBitmap.recycle();
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 无论成功失败,必须关闭Image!
        if (image != null) {
            image.close();
        }
    }
}

2. 实现YUV转Bitmap的工具方法

相机输出的YUV_420_888需要转成Bitmap能识别的格式,这里转成NV21再转Bitmap:

private Bitmap convertYuvImageToBitmap(Image image) {
    Image.Plane[] planes = image.getPlanes();
    ByteBuffer yBuffer = planes[0].getBuffer();
    ByteBuffer uBuffer = planes[1].getBuffer();
    ByteBuffer vBuffer = planes[2].getBuffer();

    int ySize = yBuffer.remaining();
    int uSize = uBuffer.remaining();
    int vSize = vBuffer.remaining();

    // YUV_420_888转NV21格式
    byte[] nv21Bytes = new byte[ySize + uSize + vSize];
    yBuffer.get(nv21Bytes, 0, ySize);
    vBuffer.get(nv21Bytes, ySize, vSize);
    uBuffer.get(nv21Bytes, ySize + vSize, uSize);

    // 把NV21转成Bitmap
    YuvImage yuvImage = new YuvImage(nv21Bytes, ImageFormat.NV21, image.getWidth(), image.getHeight(), null);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    yuvImage.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 90, outputStream);
    byte[] jpegBytes = outputStream.toByteArray();
    return BitmapFactory.decodeByteArray(jpegBytes, 0, jpegBytes.length);
}

3. 把Bitmap保存到App私有目录,传递路径到另一个Activity

直接传Bitmap风险高,不如把Bitmap存成文件,传文件路径:

private String saveBitmapToPrivateStorage(Bitmap bitmap) throws IOException {
    // 用App私有存储目录,不需要申请外部存储权限(Android 10+也能用)
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    // 创建临时文件
    File imageFile = File.createTempFile("camera_shot_", ".jpg", storageDir);
    
    // 把Bitmap写入文件
    FileOutputStream fos = new FileOutputStream(imageFile);
    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
    fos.flush();
    fos.close();
    
    return imageFile.getAbsolutePath();
}

private void jumpToDisplayActivity(String imagePath) {
    Intent intent = new Intent(this, DisplayImageActivity.class);
    intent.putExtra("EXTRA_IMAGE_PATH", imagePath);
    startActivity(intent);
}

4. 在目标Activity加载图像

在DisplayImageActivity里接收路径,加载Bitmap(记得压缩避免OOM):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_display_image);
    
    ImageView imageView = findViewById(R.id.iv_display);
    String imagePath = getIntent().getStringExtra("EXTRA_IMAGE_PATH");
    if (imagePath != null) {
        // 配置Bitmap解码选项,压缩图片防止内存溢出
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2; // 采样率,值越大图片越小,根据需求调整
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
        imageView.setImageBitmap(bitmap);
    }
}

几个关键注意事项

  • 权限:别忘了在Manifest里加相机权限<uses-permission android:name="android.permission.CAMERA"/>,并且在运行时申请。
  • 内存优化:如果你的App要处理大分辨率照片,建议用Glide、Coil这类图片加载库来加载文件路径,它们会自动处理内存缓存和回收,比手动处理更靠谱。
  • ImageReader配置:创建ImageReader时,格式一定要设为ImageFormat.YUV_420_888,不然上面的转换方法会失效。比如:
    ImageReader imageReader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888, 2);
    

内容的提问来源于stack exchange,提问作者Eman J

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:19:08