3. CameraX 核心组件详解:Preview、ImageCapture、ImageAnalysis、CameraSelector、UseCase 绑定与组合
CameraX 是 Google 推出的 Jetpack 相机库,旨在简化 Android 相机开发。其核心设计思想是 UseCase(用例) 模式——将相机功能抽象为独立的、可组合的模块。本章深入剖析 CameraX 的五大核心组件:Preview、ImageCapture、ImageAnalysis、CameraSelector,以及如何通过 ProcessCameraProvider 进行 UseCase 的绑定与组合。
3.1 CameraX 架构总览
CameraX 采用 生命周期感知 与 用例驱动 的架构。开发者无需直接管理 CameraSession,只需声明需要哪些功能(UseCase),CameraX 自动处理相机设备选择、配置、生命周期绑定与资源释放。
3.2 核心 UseCase 详解
3.2.1 Preview(预览)
Preview 负责将相机实时画面显示到 PreviewView 上。它是所有相机应用的基础 UseCase。
- 关键配置: 通过
Preview.Builder设置分辨率、目标旋转角度、相机模式(如CAMERA_EFFECT)。 - SurfaceProvider: Preview 不直接操作 SurfaceView/TextureView,而是通过
SurfaceProvider接口与 UI 层解耦。 - 性能优化: 建议使用
PreviewView(CameraX 提供的专用 View),它内部自动处理了 Surface 生命周期与缩放。
// Preview 创建与配置示例
Preview preview = new Preview.Builder()
.setTargetResolution(new Size(1920, 1080))
.setTargetRotation(Surface.ROTATION_0)
.build();
// 将 PreviewView 的 SurfaceProvider 绑定到 Preview
preview.setSurfaceProvider(previewView.getSurfaceProvider());
3.2.2 ImageCapture(拍照)
ImageCapture 提供拍照功能,支持 JPEG 和 RAW 格式输出。它是扫码相机和文档扫描应用的核心拍照组件。
- 拍照模式: 支持
CAPTURE_MODE_MINIMIZE_LATENCY(低延迟)和CAPTURE_MODE_MAXIMIZE_QUALITY(高质量)。 - 闪光灯控制: 通过
setFlashMode()设置自动、开启、关闭。 - 输出选项: 可指定文件保存路径(
OutputFileOptions)或内存中的ImageProxy。
// ImageCapture 创建与拍照示例
ImageCapture imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setFlashMode(ImageCapture.FLASH_MODE_AUTO)
.build();
// 拍照并保存到文件
File photoFile = new File(getExternalFilesDir(null), "photo.jpg");
ImageCapture.OutputFileOptions outputOptions =
new ImageCapture.OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {
// 拍照成功
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
// 拍照失败
}
});
3.2.3 ImageAnalysis(图像分析)
ImageAnalysis 提供连续的帧数据流,用于实时分析(如扫码、人脸检测、文档边缘识别)。它是扫码相机应用的核心。
- 分析模式:
STRATEGY_KEEP_ONLY_LATEST(只保留最新帧,丢弃旧帧)和STRATEGY_BLOCK_PRODUCER(阻塞生产者直到分析完成)。扫码场景推荐使用KEEP_ONLY_LATEST。 - 图像格式: 默认输出
YUV_420_888格式的ImageProxy。 - 线程管理: 分析器在独立的后台线程中执行,不会阻塞 UI 线程。
// ImageAnalysis 创建与帧分析示例
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setTargetResolution(new Size(1280, 720))
.build();
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), imageProxy -> {
// 在后台线程处理每一帧
// 对于扫码场景,将 imageProxy 传递给 ML Kit 或 ZXing 解码器
imageProxy.close(); // 必须关闭,否则会阻塞后续帧
});
imageProxy.close()。如果使用 STRATEGY_BLOCK_PRODUCER 且不关闭,会导致相机帧生产被永久阻塞。
3.2.4 CameraSelector(相机选择器)
CameraSelector 用于选择使用哪个物理相机(后置、前置、外接)。CameraX 提供了默认选择器,也支持自定义筛选逻辑。
| 选择器类型 | 说明 | 适用场景 |
|---|---|---|
DEFAULT_BACK_CAMERA |
默认后置摄像头 | 文档扫描、扫码、普通拍照 |
DEFAULT_FRONT_CAMERA |
默认前置摄像头 | 自拍、视频通话 |
| 自定义 CameraSelector | 通过 CameraSelector.Builder 添加 CameraFilter 筛选 |
需要特定镜头(如超广角、长焦) |
// 使用默认后置摄像头
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
// 自定义筛选:优先使用后置主摄,若不可用则回退
CameraSelector customSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.addCameraFilter(cameraInfos -> {
// 自定义筛选逻辑,返回最合适的 CameraInfo 列表
return cameraInfos;
})
.build();
3.3 UseCase 绑定与组合
CameraX 的核心能力在于 多 UseCase 组合。通过 ProcessCameraProvider 将多个 UseCase 绑定到同一个生命周期,系统自动协商最优配置。
3.3.1 绑定流程
- 获取
ProcessCameraProvider实例(通过ProcessCameraProvider.getInstance(context))。 - 创建所需的 UseCase 实例(Preview、ImageCapture、ImageAnalysis 等)。
- 创建
CameraSelector选择相机。 - 调用
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCases...)绑定。 - 绑定成功后返回
Camera对象,可用于控制变焦、闪光灯、对焦等。
// 完整绑定示例:同时绑定 Preview + ImageCapture + ImageAnalysis
ProcessCameraProvider cameraProvider = ProcessCameraProvider.getInstance(this).get();
// 创建 UseCase
Preview preview = new Preview.Builder().build();
ImageCapture imageCapture = new ImageCapture.Builder().build();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
// 选择后置摄像头
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
// 绑定到生命周期
Camera camera = cameraProvider.bindToLifecycle(
this, // LifecycleOwner
cameraSelector, // 相机选择器
preview, // 预览
imageCapture, // 拍照
imageAnalysis // 图像分析
);
// 绑定后设置 Preview 的 SurfaceProvider
preview.setSurfaceProvider(previewView.getSurfaceProvider());
3.3.2 UseCase 组合策略
不同的应用场景需要不同的 UseCase 组合。以下是常见组合及其适用场景:
| 组合模式 | 包含 UseCase | 典型应用 | 注意事项 |
|---|---|---|---|
| 基础预览 | Preview | 简单相机预览 | 最低资源消耗 |
| 扫码相机 | Preview + ImageAnalysis | 二维码/条形码扫描 | ImageAnalysis 使用 KEEP_ONLY_LATEST 策略 |
| 文档扫描 | Preview + ImageCapture + ImageAnalysis | 文档边缘检测 + 拍照 | ImageAnalysis 用于实时边缘检测,ImageCapture 用于最终拍照 |
| 高清拍照 | Preview + ImageCapture | 普通拍照应用 | ImageCapture 使用 MAXIMIZE_QUALITY 模式 |
| 视频录制 | Preview + VideoCapture | 视频录制应用 | VideoCapture 需要额外配置 MediaRecorder |
3.4 UseCase 冲突与协商机制
当绑定多个 UseCase 时,CameraX 会自动进行 配置协商,确保所有 UseCase 都能正常工作。如果存在不可调和的冲突,bindToLifecycle 会抛出 IllegalArgumentException。
- 同时绑定两个
ImageCapture或两个ImageAnalysis(同一类型 UseCase 只能有一个)。 - 绑定的 UseCase 要求的分辨率或帧率超出相机硬件能力。
- 同时绑定
VideoCapture和ImageAnalysis时,某些设备可能不支持同时输出两种格式。
3.4.1 协商优先级
CameraX 的协商策略遵循以下优先级:
- Preview 的分辨率优先满足
PreviewView的尺寸。 - ImageCapture 的分辨率优先满足设定的
targetResolution,若无法满足则降级。 - ImageAnalysis 的分辨率在满足前两者后取剩余能力,通常建议设置较小的分辨率(如 640x480)以提高分析速度。
3.5 生命周期管理与解绑
CameraX 自动感知 LifecycleOwner 的生命周期:
- onStart: 打开相机会话。
- onResume: 恢复预览和帧分析。
- onPause: 暂停预览和帧分析。
- onStop: 关闭相机会话,释放资源。
- onDestroy: 完全解绑 UseCase。
如果需要手动解绑(例如切换相机),调用 cameraProvider.unbindAll() 或 cameraProvider.unbind(useCases...)。
// 手动解绑所有 UseCase
cameraProvider.unbindAll();
// 解绑特定 UseCase(例如只移除 ImageAnalysis)
cameraProvider.unbind(imageAnalysis);
// 解绑后重新绑定新的 UseCase 组合
Camera camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture
);
3.6 本章小结
- Preview 负责实时预览,通过
SurfaceProvider与 UI 解耦。 - ImageCapture 提供拍照功能,支持低延迟和高质量两种模式。
- ImageAnalysis 提供连续帧分析,是扫码和文档检测的核心。
- CameraSelector 用于选择后置/前置/自定义相机。
- UseCase 绑定 通过
ProcessCameraProvider.bindToLifecycle()实现,支持灵活组合。 - CameraX 自动处理 配置协商 和 生命周期管理,大幅降低开发复杂度。