2. Android 相机开发基础:Camera API 演进史
在 Android 平台上,相机开发经历了从底层硬件控制到高层抽象封装的演进过程。理解 Camera API 的演进史,是掌握现代相机框架的基石。本章将系统梳理 Camera1、Camera2 与 CameraX 的核心差异、架构设计与生命周期管理策略。
2.1 Camera API 演进全景
Android 相机 API 的演进主要分为三个阶段,每个阶段都解决了前一阶段的核心痛点,同时引入了新的能力与复杂度。
| 特性维度 | Camera1 (API 1) | Camera2 (API 2) | CameraX (Jetpack) |
|---|---|---|---|
| 引入版本 | Android 1.0 (API 1) | Android 5.0 (API 21) | Android 5.0+ (通过 Jetpack) |
| 架构模式 | 单一 Camera 对象,同步回调 | CameraManager + CameraDevice + CaptureSession,异步管道 | UseCase 驱动,生命周期感知,自动配置 |
| 线程模型 | 主线程回调,易 ANR | 独立 HandlerThread,异步回调 | 内部协程/线程池,自动管理 |
| 设备兼容性 | 厂商定制差异大,需大量兼容代码 | 需实现 CameraStateCallback,兼容性仍复杂 | 内置 CameraXConfig 与厂商扩展,开箱即用 |
| 生命周期 | 手动绑定 Activity/Fragment 生命周期 | 手动管理打开/关闭/释放 | 自动绑定 LifecycleOwner,自动释放 |
| 预览/拍照/分析 | 通过 SurfaceHolder 或 SurfaceView 实现 | 通过 OutputConfiguration 配置多个 Surface | 预置 Preview / ImageCapture / ImageAnalysis UseCase |
| 适用场景 | 简单拍照、老设备兼容 | 专业相机、手动控制、RAW 输出 | 快速集成、扫码、文档扫描、通用相机应用 |
选型建议: 对于本课程涉及的扫码相机与文档扫描应用,CameraX 是最佳选择。它大幅降低了开发复杂度,同时保留了足够的扩展性。仅在需要极致手动控制(如专业摄影 App)时才考虑 Camera2。
2.2 Camera1 架构与局限
Camera1 是 Android 最早的相机 API,采用 同步阻塞式 设计。开发者通过 android.hardware.Camera 类直接控制硬件。
核心流程: open() → setParameters() → setPreviewDisplay() → startPreview() → takePicture() → stopPreview() → release()
Camera1 的主要痛点:
- 线程阻塞:
takePicture()的回调发生在主线程,长时间处理会导致 ANR。 - 状态机脆弱: 必须严格按照 open → preview → capture 的顺序调用,错误顺序直接崩溃。
- 厂商碎片化: 不同厂商对
Camera.Parameters的支持程度不同,需要大量 if-else 兼容代码。 - 缺乏流控制: 无法同时进行预览、拍照和分析,只能通过切换 Surface 实现。
2.3 Camera2 管道架构
Camera2 从 Android 5.0 开始引入,彻底重构了相机架构。它采用 异步管道 模型,将相机设备抽象为 CameraDevice,将捕获请求抽象为 CaptureRequest,将输出目标抽象为 Surface。
核心概念: CameraManager(发现设备)→ CameraDevice(连接设备)→ CaptureRequest(配置参数)→ CameraCaptureSession(提交请求)→ CaptureCallback(异步结果)
Camera2 的关键改进:
- 异步非阻塞: 所有操作通过回调返回,不会阻塞主线程。
- 可配置管道: 支持同时输出到多个 Surface(预览 + 拍照 + 分析)。
- 手动控制: 支持 ISO、快门速度、对焦距离、RAW 输出等专业参数。
- 流模型: 通过
StreamConfigurationMap查询设备支持的输出尺寸和格式。
Camera2 的陷阱: 虽然 Camera2 功能强大,但需要开发者手动处理大量状态回调(onOpened / onDisconnected / onError),并且不同厂商对 Camera2 的实现存在差异。对于扫码和文档扫描这类应用,直接使用 Camera2 会导致代码量膨胀且难以维护。
2.4 CameraX 的优势与架构
CameraX 是 Google 在 2019 年推出的 Jetpack 相机库,它基于 Camera2 封装,但提供了更高级的抽象。CameraX 的核心设计理念是 UseCase 驱动 和 生命周期感知。
2.4.1 CameraX 核心优势
- 开箱即用: 内置 Preview、ImageCapture、ImageAnalysis 三个预置 UseCase,覆盖 90% 的相机场景。
- 生命周期感知: 自动绑定 LifecycleOwner,在 onStart 时打开相机,在 onStop 时释放资源。
- 设备兼容性: 通过 CameraXConfig 自动适配不同厂商的 Camera2 实现差异。
- 扩展性: 支持 CameraX Extensions(HDR、夜景、美颜等),无需手动实现。
- 线程安全: 内部使用协程和线程池,开发者无需关心线程切换。
2.4.2 CameraX 软件架构
2.4.3 核心 UseCase 详解
| UseCase | 功能 | 输出类型 | 典型场景 |
|---|---|---|---|
| Preview | 实时预览 | SurfaceProvider (TextureView / SurfaceView) | 取景器显示 |
| ImageCapture | 拍照 | ImageProxy / Bitmap / File | 文档拍照、扫码结果 |
| ImageAnalysis | 帧分析 | ImageProxy (YUV_420_888) | 扫码解码、文档边缘检测 |
多 UseCase 同时运行: CameraX 支持同时绑定多个 UseCase。例如,扫码应用可以同时绑定 Preview(显示取景器)和 ImageAnalysis(帧分析),两者共享同一个相机管道,无需手动切换。
2.5 生命周期感知机制
CameraX 的生命周期管理是其最核心的特性之一。它通过 ProcessCameraProvider 与 LifecycleOwner 绑定,自动响应 Activity/Fragment 的生命周期事件。
2.5.1 生命周期绑定流程
- 创建 Provider:
ProcessCameraProvider.getInstance(context)获取单例。 - 绑定生命周期:
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCases)。 - 自动管理: 当 LifecycleOwner 进入 STARTED 状态时,相机自动打开;进入 STOPPED 状态时,相机自动释放。
- 配置变更: 屏幕旋转或配置变更时,LifecycleOwner 会重建,CameraX 自动处理相机的重新绑定。
2.5.2 生命周期状态映射
2.5.3 代码示例:生命周期绑定
class CameraXLifecycleFragment : Fragment() {
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var previewUseCase: Preview
private lateinit var imageAnalysisUseCase: ImageAnalysis
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
bindCameraUseCases()
}, ContextCompat.getMainExecutor(requireContext()))
}
private fun bindCameraUseCases() {
// 1. 配置 Preview UseCase
previewUseCase = Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.build()
// 2. 配置 ImageAnalysis UseCase(用于扫码)
imageAnalysisUseCase = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysisUseCase.setAnalyzer(Executors.newSingleThreadExecutor()) { imageProxy ->
// 帧分析逻辑
imageProxy.close()
}
// 3. 绑定到生命周期
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(
this, // LifecycleOwner
cameraSelector, // 前置/后置
previewUseCase, // 预览
imageAnalysisUseCase // 分析
)
}
override fun onDestroyView() {
super.onDestroyView()
// CameraX 自动在 ON_STOP 时释放,无需手动调用
}
}
最佳实践: 在
bindToLifecycle() 中传入的 UseCase 顺序会影响优先级。通常将 Preview 放在第一位,确保预览流畅。ImageAnalysis 的 STRATEGY_KEEP_ONLY_LATEST 策略可以避免帧积压,适合扫码场景。
2.6 本章小结
- Camera1 是同步阻塞模型,适合简单场景但已不推荐新项目使用。
- Camera2 是异步管道模型,功能强大但复杂度高,适合专业相机应用。
- CameraX 基于 Camera2 封装,提供 UseCase 驱动和生命周期感知,是扫码相机与文档扫描应用的最佳选择。
- 生命周期管理是 CameraX 的核心优势,通过
bindToLifecycle()自动处理相机的打开与释放,大幅减少样板代码。