一、CameraX 简介与架构概述

做 Android 相机开发好几年了,我踩过不少坑。最头疼的就是设备碎片化——同样的代码,在小米上跑得飞快,到华为上就黑屏,再到三星又给你来个「相机服务异常」。后来 Google 推出了 CameraX,说实话,一开始我是拒绝的,总觉得「又一个封装层,性能肯定打折扣」。

但当我真在项目里用上它,嗯,态度变了。不仅是启动流程简化了,连 lifecycle 绑定都自动搞定。今天咱们就来聊聊 CameraX 到底是什么,它背后的架构长什么样。

1.1 什么是 CameraX?

CameraX 是 Android Jetpack 的官方相机库。它直接基于 Camera2 API 构建,但把那些繁琐的 Surface 创建、会话管理、状态机流转都封装掉了。

特性说明
目标降低相机开发复杂度,消除设备兼容性问题
底层依赖Camera2 API(兼容到 API 21)
核心优势自动绑定 Lifecycle、多 UseCase 同时运行、内置兼容层
版本演进1.0(基础)、1.1(扩展)、1.2(ML Kit 集成)

说白了,你写 Camera2 原本要写四五百行的配置代码,用 CameraX 可能二三十行就够了。我记得第一次迁移时还有点不信,结果跑起来发现预览、拍照、分析同时开,而且还没崩——那一刻,我是真的服了。

1.2 架构分层

CameraX 并不是一个黑盒子。它内部有清晰的分层,我把它梳理成下面这张图:

CameraX 架构分层 App 层(Activity / Fragment / Service) CameraX 核心层 Lifecycle + CameraProvider UseCase(Preview/ImageCapture/ImageAnalysis) CameraSelector + 配置 Camera2 桥接层(自动兼容 + 扩展厂商) HAL / 硬件摄像头驱动 CameraX 通过 Lifecycle 感知页面状态,自动绑定/解绑 UseCase,无需手动处理 onPause / onResume

从上往下看,App 层只需要调用 ProcessCameraProvider.getInstance() 绑定一个 Lifecycle,然后创建 UseCase 并绑定到同一个生命周期。下层的事情——创建 CameraCaptureSession、配置请求、处理回调——CameraX 全部帮你搞定。

你可能会问:「那它和 Camera2 的对应关系是怎样的?」 我刚开始也有这个疑惑。简单说,一个 Preview UseCase 背后就是一个 Camera2 的 CameraCaptureSession 重复请求,ImageCapture 对应 CaptureRequest 的 CONTROL_AE_PRECAPTURE 状态机。你用 CameraX 写代码时,其实是在操作更高层的语义,底层映射交给了框架。

1.3 核心组件详解

我把最常用的几个组件单独拿出来,说清楚它们分别干什么:

  • ProcessCameraProvider —— 生命周期感知的 CameraX 入口。它像是一个单例管理器,负责把 Camera 对象和 LifecycleOwner 绑定。我习惯在 onCreate 里就获取它的实例,避免重复创建。
  • Preview —— 预览画面 UseCase。它接收一个 SurfaceProvider,你给它一个 PreviewView 或者自定义 Surface,它就持续往上面投数据。
  • ImageCapture —— 拍照 UseCase。支持 takePicture() 带回调,也可以保存到文件。注意,它默认会做 JPEG 压缩,你可以设置 setCaptureMode(CaptureMode.MIN_LATENCY) 来追求速度。
  • ImageAnalysis —— 帧分析 UseCase。常用于二维码扫描、人脸检测等。它的回调频率可以调 setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST),保证不丢帧但不过载。
  • CameraSelector —— 选择摄像头。默认 CameraSelector.DEFAULT_BACK_CAMERA 和 CameraSelector.DEFAULT_FRONT_CAMERA,你也可以根据镜头方向、感光特性自定义。
UseCase 对比
UseCase输出类型典型场景性能敏感到?
PreviewSurface / TextureView取景框帧率、分辨率切换
ImageCaptureJPEG byte[] / File拍照拍照延迟、HDR 处理
ImageAnalysisImageProxyAI 推理、扫码分析频率、内存拷贝

1.4 生命周期绑定——自动管理的背后

我说一个真实的事。有个同事用 Camera2 写了一个扫码页面,测试时好好的,等放到线上,用户反馈「扫一两次就黑屏」。查了半天,原来是 Activity 重建时没有正确释放 Camera 资源。用 CameraX 以后,这种问题再也没出现过。

它怎么做到的?核心是 Lifecycle.addObserver()。你把 ProcessCameraProvider 绑定到当前页面的 Lifecycle,CameraX 内部监听 ON_CREATE、ON_START、ON_RESUME、ON_PAUSE、ON_DESTROY,自动调 Camera.open() 和 Camera.close()。你想想看,这得省多少模板代码。

// 一个典型的 CameraX 初始化
val processCameraProvider = ProcessCameraProvider.getInstance(this).get()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
val preview = Preview.Builder().build()
val imageAnalysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()

processCameraProvider.bindToLifecycle(
    this,               // LifecycleOwner
    cameraSelector,
    preview,
    imageAnalysis
)

我的习惯: 把 bindToLifecycle 写在一个顶层函数里,传 LifecycleOwner 和 UseCase 列表。这样即使页面需要动态改变 UseCase(比如从预览切到拍照模式),也只需要重新 bind,旧的会自动解绑。

1.5 避坑指南——我踩过几次的坑

说两个我印象深的。

  • 设备兼容性问题: 有些老机型(比如 API 21-23)的 Camera2 实现有 bug,CameraX 虽然内置了兼容层,但部分扩展效果(如 HDR、夜景)需要厂商支持。我曾经在低端机上跑 ImageCapture.Mode.MAX_QUALITY 模式,结果拍照后卡住 2 秒。后来改成 MIN_LATENCY 就好了。
  • UseCase 冲突: 同时绑定 Preview 和 ImageAnalysis 时,如果它们需要的分辨率或帧率不兼容,CameraX 会自动降级。有一次我把 Preview 设为最大 1080p,ImageAnalysis 设为 480p,结果预览始终只有 480p 清晰度。最后发现是 CameraX 为了省资源,取了两者的最小值。解决方案:手动配置 SupportedOutputSizes 强制锁定。

特别注意: CameraX 默认会对 UseCase 做 ViewPort 裁剪,如果你要覆盖全屏预览,必须在 Preview.Builder 里设置 setTargetAspectRatio(WALL) 或者自定义 setTargetResolution()。否则你会看到黑边或画面被截断。

1.6 小结

CameraX 的架构可以总结为一句话:简化 Camera2 的复杂度,用 Lifecycle 自动管理相机状态。它把底层细节藏在 CameraXCore 中,开发者只需要关心「用哪个 UseCase」「输出什么画面」。接下来我们会深入到启动流程的每一段耗时,看看它到底慢在哪里、怎么优化。

本章要点速记:

  • ProcessCameraProvider 是唯一入口,负责绑定 Lifecycle
  • Preview / ImageCapture / ImageAnalysis 三大核心 UseCase
  • CameraSelector 选择前后摄
  • 自动生命周期管理:无惧重建、黑屏
  • 兼容层解决大部分碎片化问题,但极端机型仍需兜底