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 实现。
Camera1 同步阻塞架构 App 主线程 Camera 对象 Camera HAL 同步调用 硬件控制 回调全部在主线程执行 → 拍照处理耗时 → ANR 风险 状态机严格:open → startPreview → takePicture → stopPreview → release

2.3 Camera2 管道架构

Camera2 从 Android 5.0 开始引入,彻底重构了相机架构。它采用 异步管道 模型,将相机设备抽象为 CameraDevice,将捕获请求抽象为 CaptureRequest,将输出目标抽象为 Surface。

核心概念: CameraManager(发现设备)→ CameraDevice(连接设备)→ CaptureRequest(配置参数)→ CameraCaptureSession(提交请求)→ CaptureCallback(异步结果)

Camera2 的关键改进:

  • 异步非阻塞: 所有操作通过回调返回,不会阻塞主线程。
  • 可配置管道: 支持同时输出到多个 Surface(预览 + 拍照 + 分析)。
  • 手动控制: 支持 ISO、快门速度、对焦距离、RAW 输出等专业参数。
  • 流模型: 通过 StreamConfigurationMap 查询设备支持的输出尺寸和格式。
Camera2 异步管道架构 CameraManager CameraDevice CaptureRequest CaptureSession 异步回调:onOpened / onDisconnected / onError / onCaptureCompleted 预览 Surface TextureView / SurfaceView 拍照 Surface ImageReader / JPEG 分析 Surface ImageAnalysis / YUV 一个 CaptureRequest 可同时绑定多个 Surface 输出
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 软件架构

CameraX 分层架构 应用层:Preview / ImageCapture / ImageAnalysis UseCase CameraX 核心层:UseCase 绑定 / 生命周期管理 / 配置解析 Camera2 适配层:CameraDevice / CaptureSession / Surface 封装 Camera2 HAL 层:厂商驱动 / 硬件抽象层 开发者只需关注 UseCase 配置,无需处理 Camera2 底层状态机

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 生命周期绑定流程

  1. 创建 Provider: ProcessCameraProvider.getInstance(context) 获取单例。
  2. 绑定生命周期: cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCases)。
  3. 自动管理: 当 LifecycleOwner 进入 STARTED 状态时,相机自动打开;进入 STOPPED 状态时,相机自动释放。
  4. 配置变更: 屏幕旋转或配置变更时,LifecycleOwner 会重建,CameraX 自动处理相机的重新绑定。

2.5.2 生命周期状态映射

CameraX 生命周期状态映射 ON_CREATE ON_START ON_RESUME ON_STOP CameraX 在 ON_START 时打开相机,在 ON_STOP 时释放相机 配置变更(如屏幕旋转)时,LifecycleOwner 重建,CameraX 自动重新绑定

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() 自动处理相机的打开与释放,大幅减少样板代码。