1. CameraX基础

说实话,我刚接触 Android Camera 时,直接对着 Camera2 的 StateMachine 崩溃了好几次。API 回调层层嵌套,Session 状态机像迷宫,一个预览启动写了两百行还没跑通……后来 Google 推出了 CameraX,我当时的第一反应是:又是个套壳封装吧?结果试用后,真香。

CameraX 不是对 Camera2 的简化包装,而是基于 Camera2 构建的生命周期感知型相机库。它把 Camera2 里那些让人头疼的CameraDevice、CameraCaptureSession、CaptureRequest全藏起来了,直接给你扔出四个 UseCase:Preview、ImageCapture、ImageAnalysis、VideoCapture。你只需要告诉它「我要拍照」,不用管底层的 Session 怎么重建、配置怎么切换。

我个人觉得,CameraX 最大的价值在于自动处理设备兼容性。Camera2 下你要针对不同厂商写黑名单、调参数,CameraX 内部有个 CameraXConfig 的 Provider 机制,很多坑已经被官方踩过了。

核心组件一览

刚入门的同学可能觉得这四个组件名字差不多,其实分工很明确:

组件 作用 典型使用场景
Preview 将相机画面实时输出到 SurfaceView/TextureView 扫码界面、直播预览
ImageCapture 拍摄单张照片,支持 JPEG 或 RAW 拍照按钮、连拍(需扩展)
ImageAnalysis 持续获取 YUV_420_888 帧,供分析器处理 人脸检测、条形码识别
VideoCapture 录制视频(1.0 版本后稳定) 短视频、监控

注意:这四个组件你可以同时绑定多个。比如预览的同时做 ImageAnalysis,甚至再绑一个 ImageCapture 做拍照。但要注意分辨率冲突,你想想看,预览需要 1080p,分析却要 640×480,CameraX 会自动帮你协调——这是 Camera2 下很难自动做的事情。

与 Camera2 的关系

很多人问:我学了 Camera2 还需要学 CameraX 吗?我的回答是:如果你不需要极度底层的控制(比如自定义 RAW 处理、多物理摄像头同时流),CameraX 完全够用。而且 CameraX 在你写的每一行代码背后,依然会转换成 Camera2 的调用。你可以把 CameraX 看作一个自动挡,Camera2 是手动挡。

我曾经在一个项目里直接用 Camera2 写了一个扫码页面,结果在华为某款机型上预览绿屏,排查了三天发现是 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP 里缺少某个尺寸组合。换成 CameraX 后,一行 PreviewView 配置完事,设备兼容性直接拉满。

核心结论:CameraX 是 Camera2 的「开发者友好层」,它把生命周期、设备兼容性、参数协调这三类脏活全包了。

架构逻辑:不是松散的组合,是绑定的链路

CameraX 的架构可以总结为三句话:

  1. LifecycleOwner 绑定相机生命周期——Activity/Fragment 销毁时自动释放。
  2. UseCase 绑定到同一个 Camera——你不需要手动打开摄像头,ProcessCameraProvider 会帮你选一个最佳 Camera。
  3. UseCase 之间自动协调输出尺寸和帧率——比如你同时绑了 Preview 和 ImageAnalysis,CameraX 会尽量找两者都支持的公共分辨率,避免重新配置。

下面这张图是我画的核心关系:

CameraX 架构核心组件与关系 LifecycleOwner ProcessCameraProvider CameraSelector UseCase 组 (协同调度) Preview ImageCapture ImageAnalysis VideoCapture PreviewView / Surface 📌 关键点 所有 UseCase 绑定到同一个 CameraProvider,由 LifecycleOwner 统一管理生命周期 CameraSelector 负责选择前/后摄,UseCase 组自动协调输出分辨率

我的小习惯:写 CameraX 代码的第一步永远是创建 ProcessCameraProvider.getInstance(context),然后绑定到 lifecycleOwner。这样后面的 UseCase 配置都在这两个对象上完成,后期维护一眼就能看清生命周期范围。

核心组件逐个看

Preview:最简单的预览

你只需要一个 PreviewView(在 androidx.camera.view 包下),然后声明 Preview UseCase,设置 SurfaceProvider 即可。我曾经踩过一个坑:没有在 Preview.Builder 里设置 setTargetResolution(),结果某些设备上预览画面被拉伸。正确的做法是:

val preview = Preview.Builder()
    .setTargetResolution(Size(1920, 1080))  // 给个参考尺寸,不是强制
    .build()
preview.setSurfaceProvider(binding.previewView.surfaceProvider)

ImageCapture:拍照的几种姿势

拍照接口就那么几个:takePicture(Context, OnImageSavedCallback) 存文件,takePicture(ImageCapture.OnImageCapturedCallback) 拿内存里的 ImageProxy。我一般用前者,因为文件回调更稳定。注意要把 ImageCapture 的 flash 模式处理好,不然暗光下拍出来全是黑的。

ImageAnalysis:处理每一帧的注意点

这个组件会在后台持续回调 analyzer(image: ImageProxy)。你要自己调用 image.close(),否则流会被卡住——这也是新人最容易犯的错。另外,如果想做高帧率分析,记得设置 setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST),不然队列会越积越多。

我曾经遇到过的坑:在 ImageAnalysis 里做耗时操作(比如数据库写入),导致帧率暴跌。后来我用 ExecutorService 把分析任务丢到独立线程,才解决。记住,analyzer 默认跑在 HandlerThread 上,别堵它。

VideoCapture:视频录制虽迟但稳

CameraX 的 VideoCapture 在 1.0.0 版本后进入稳定。用法和 Preview 类似,绑定一个 VideoCapture.Builder(),然后通过 videoCapture.startRecording() 启动。唯一的限制:你不能同时使用 ImageCapture 和 VideoCapture 进行高分辨率录像+拍照,因为底层 Camera2 的 Stream 配置会冲突——CameraX 会给你报一个 UseCaseGroup 配置异常。我的解决方案:要么只选一个 UseCase,要么降低分辨率。

val videoCapture = VideoCapture.Builder()
    .setBitRate(10_000_000)
    .setVideoFrameRate(30)
    .build()
binding.previewView.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
// 绑定到 CameraX 时必须同时绑定 Preview 和 VideoCapture
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture)

快速对比 CameraX vs Camera2

维度 CameraX Camera2
核心概念 UseCase、Lifecycle、CameraProvider CameraDevice、CaptureSession、Request
生命周期管理 自动绑定 LifecycleOwner 手动 onResume/onPause 处理
设备兼容性 内置厂商兼容库(CameraXConfig) 需自行写黑名单/调参数
多 UseCase 协调 自动找公共尺寸 自己算 StreamConfigurationMap
性能开销 略高(封装层) 极低(直接调用 HAL)
自定义程度 中等 完全控制

就本章而言,你只要记住:CameraX 是用空间换时间、用抽象换兼容性的框架。如果你要快速交付一个相机功能,选它准没错。