针对 Camera2 框架中这四个最常见的“顽疾”,其根本原因通常都出在流水线(Pipeline)的阻塞、状态错乱或资源未释放。
以下是针对每个问题的核心原因分析与排查止血方案:
1. 预览卡顿 (Preview Lag / Stutter)
预览卡顿通常是因为主线程(UI线程)被阻塞,或者相机输出的分辨率过高导致硬件处理不过来。
核心原因排查:
Handler 绑定错误:检查
openCamera、createCaptureSession和setRepeatingRequest中传入的最后一个参数Handler。如果传了null或new Handler(Looper.getMainLooper()),相机的所有回调(包括每一帧的数据解析)都会挤在主线程执行。分辨率不匹配:输出的
Surface(如TextureView)的分辨率与相机支持的预览分辨率(通过StreamConfigurationMap获取)不匹配,导致系统在底层频繁进行缩放(Scaling)操作。数据处理太重:如果在
ImageReader.OnImageAvailableListener中直接写了耗时的图像分析算法(如人脸识别、格式转换),且没有异步处理,会直接拖慢整条流水线。
解决方案:
使用独立后台线程:必须创建一个专门的
HandlerThread供相机后台回调使用。及时关闭 Image:在
onImageAvailable中,处理完数据后必须立刻调用image.close(),否则下一帧无法塞入队列,导致流水线阻塞。
2. 对焦抖动 (Focus Hunting / Jitter)
对焦抖动表现为画面频繁忽明忽暗、反复拉伸风箱,无法稳定锁定焦点。
核心原因排查:
对焦模式选错:在预览的
CaptureRequest中,错误地在CONTROL_AF_MODE_AUTO(手动触发对焦)和CONTROL_AF_MODE_CONTINUOUS_PICTURE(持续自动对焦)之间频繁切换或混用。重复触发对焦触发器:在
setRepeatingRequest的循环请求中,错误地写入了CaptureRequest.CONTROL_AF_TRIGGER_START。这会导致相机每一帧都在重新初始化对焦流程。
解决方案:
保持持续对焦:日常预览时,只需设置一次
CONTROL_AF_MODE_CONTINUOUS_PICTURE,不要带上AF_TRIGGER。正确手动对焦(测光):触发点击对焦时,先发送一个单次请求(capture)去触发
CONTROL_AF_TRIGGER_START,等待CaptureResult返回对焦成功(AF_STATE_FOCUSED_LOCKED)后,立刻将触发器重置为CONTROL_AF_TRIGGER_IDLE发回预览,否则相机会锁死在当前状态。
3. 内存泄漏 (Memory Leak)
Camera2 涉及大量系统底层硬件服务的映射,一旦发生内存泄漏,轻则手机发烫,重则下次打开相机直接报 CameraAccessException: Camera in use 或系统 OOM。
核心原因排查:
生命周期未闭环:Activity/Fragment 销毁时,没有依次关闭会话和设备。
异步回调持有 Context:传入相机的自定义
StateCallback或Handler隐式持有了外部 Activity 的引用,导致 Activity 无法被 GC 回收。
解决方案(标准销毁序列):
在
onPause()或onDestroy()中,必须严格执行以下反向释放流程:// 1. 停止预览循环 captureSession?.stopRepeating() // 2. 关闭会话 captureSession?.close() captureSession = null // 3. 关闭相机设备 cameraDevice?.close() cameraDevice = null // 4. 关闭 ImageReader 等资源 imageReader?.close() // 5. 退出后台线程 backgroundThread?.quitSafely()
4. 拍照黑屏 (Black Screen on Capture)
点击拍照瞬间,画面突然变黑,或者最终保存出来的图片是一张全黑图。
核心原因排查:
Surface 目标丢失:在构建拍照的
CaptureRequest时,忘记添加拍照的输出端(如ImageReader.surface),或者错误地把预览的Surface当作了拍照的输出端。闪光灯与曝光同步失败:开启闪光灯拍照时,没有进行前置测光(Pre-capture Metering),导致相机在闪光灯还没亮起或已经熄灭的瞬间闭合了快门。
底层硬件配置级别不够(Legacy 设备):部分老旧手机硬件级别为
LEGACY,它们不支持在拍照时同时维持预览输出。一旦调用capture(),预览流会被强制中断,处理不当就会黑屏。
解决方案:
检查 Target:确保拍照的 Request 中包含了正确的
ImageReader.surface:photoBuilder.addTarget(imageReader.surface)。标准拍照预检查流水线:如果是高要求拍照,遵循官方标准流程:
发送
AF_TRIGGER_START锁定对焦。发送
AE_PRECAPTURE_TRIGGER_START锁定曝光与闪光灯测光。收到测光完成回调后,再发送最终的拍照
capture()。拍照完成后,发送
AE/AF_TRIGGER_CANCEL恢复预览。
💡 一个终极排查建议:利用官方工具
如果你被 Camera2 的复杂状态搞晕了,推荐在代码中引入 CameraX 的 Camera2Interop 工具,或者在排查阶段直接使用 Google 官方的开源相机库 Camera2Basic 作为标准参照物。通过比对日志中 CaptureResult 的状态机变化,可以极大地缩短 Bug 排查周期。