Camera2 API中如何让ImageReader的OnImageAvailableListener实时回调
Camera2 API实现ImageReader实时捕获图像流的解决方案
我来帮你搞定这个问题~先给你理清楚核心原因:基础Camera2示例里用的是TEMPLATE_STILL_CAPTURE(静态拍照模板),只会触发单次捕获,所以OnImageAvailableListener只调用一次。要实现实时连续的图像流,得让相机持续输出图像到ImageReader,核心步骤如下:
1. 正确配置ImageReader
首先要确保ImageReader的参数和相机输出匹配,同时设置合理的缓存数量:
- 尺寸:和相机预览/捕获的尺寸保持一致(可以从
CameraCharacteristics里获取支持的尺寸列表选合适的) - 格式:推荐用
ImageFormat.YUV_420_888(相机输出的原生格式,性能好) - maxImages:设置2-3即可(太小容易阻塞,太大占用内存)
示例代码:
// 假设已经获取到合适的预览尺寸previewSize ImageReader imageReader = ImageReader.newInstance( previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2 // 缓存图像数量,按需调整 );
2. 配置OnImageAvailableListener并后台处理图像
监听器回调的线程不要做耗时操作,否则会阻塞相机流。建议把图像处理逻辑放到后台线程(比如用HandlerThread),并且务必用完Image后调用image.close()释放资源,不然会占满ImageReader的缓存,导致无法获取新图像。
示例代码:
// 先创建后台线程和Handler HandlerThread backgroundThread = new HandlerThread("ImageProcessingThread"); backgroundThread.start(); Handler backgroundHandler = new Handler(backgroundThread.getLooper()); // 设置监听器 imageReader.setOnImageAvailableListener((reader) -> { // 获取最新的图像,跳过旧的避免积压 Image image = reader.acquireLatestImage(); if (image == null) return; // 把图像处理任务抛到后台线程 backgroundHandler.post(() -> { try { // 这里写你的图像处理逻辑,比如转换为Bitmap、做AI识别等 processCapturedImage(image); } finally { // 必须关闭Image,释放相机资源 image.close(); } }); }, null); // 第二个参数传null的话,回调会在ImageReader的内部线程触发
3. 启动相机的重复捕获请求
关键一步:用CameraCaptureSession.setRepeatingRequest()替代单次的capture(),并且把ImageReader的Surface加入到捕获请求的输出目标中。推荐用TEMPLATE_PREVIEW模板(适合实时预览流),也可以用TEMPLATE_RECORD(适合录像场景)。
示例代码:
// 准备输出Surface列表:包含预览的Surface(比如TextureView的Surface)和ImageReader的Surface List<Surface> outputSurfaces = new ArrayList<>(); outputSurfaces.add(previewTextureView.getSurface()); // 预览用的Surface outputSurfaces.add(imageReader.getSurface()); // 加入ImageReader的Surface // 创建CaptureSession cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { try { // 创建预览模板的捕获请求 CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 添加所有输出目标 requestBuilder.addTarget(previewTextureView.getSurface()); requestBuilder.addTarget(imageReader.getSurface()); // 配置自动对焦(可选,根据需求调整) requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 启动重复捕获请求,相机就会持续输出图像流了 session.setRepeatingRequest( requestBuilder.build(), null, // 捕获回调,不需要可以传null backgroundHandler // 捕获请求的执行线程,也可以传mainHandler ); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { // 处理配置失败的逻辑,比如弹提示 Toast.makeText(context, "相机配置失败", Toast.LENGTH_SHORT).show(); } }, null);
几个关键注意事项
- 权限:确保已经申请了
CAMERA权限,Android 6.0及以上需要动态申请 - 尺寸匹配:ImageReader的尺寸必须和捕获请求的尺寸一致,否则相机无法输出图像到ImageReader
- 内存管理:绝对不能忘记调用
image.close(),否则会导致ImageReader缓存耗尽,相机流中断 - 线程安全:后台处理线程建议用单线程的
HandlerThread,避免多线程操作图像带来的问题
内容的提问来源于stack exchange,提问作者HimalayanCoder
相关产品推荐
相关产品推荐

