基于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
相关产品推荐
相关产品推荐

