CameraX 库解析:设计目标、UseCase 模型与生命周期绑定

各位同学,今天我们来聊聊 CameraX。说实话,这个库刚出来的时候,我内心是有点抵触的——又一套 API?后来真在项目里用上了,才觉得「真香」。CameraX 的设计初衷,说白了就是帮我们省掉那些重复的、容易出错的相机开发工作。

一、CameraX 的设计目标

Google 推出 CameraX,目标很明确:让相机开发变得简单、一致、可维护。我总结下来,核心就三点:

  • 易用性:开发者不用再跟 Camera1 的繁琐回调、Camera2 的状态机死磕。几行代码就能跑起来预览。
  • 生命周期感知:自动绑定 Activity/Fragment 的生命周期。你想想看,以前我们得手动在 onResume 打开相机、onPause 关闭相机,稍不留神就泄漏了。
  • 设备兼容性:CameraX 内部做了大量兼容工作。我在项目中遇到过,同一款手机用 Camera2 拍照颜色偏绿,换 CameraX 就正常了——因为它自动应用了厂商的兼容补丁。

一句话总结:CameraX 是 Google 对 Camera2 的「上层封装」,目标是让 90% 的相机需求用最少的代码实现。

二、UseCase 模型:三个核心用例

CameraX 把相机功能抽象成几个独立的 UseCase。我个人习惯把 UseCase 理解为「相机能力的积木块」——你需要什么功能,就拼什么积木。

UseCase 功能 典型场景
Preview 取景预览 扫码界面、自拍预览
ImageAnalysis 逐帧图像分析 人脸检测、条形码识别
VideoCapture 视频录制 录像功能、直播推流
ImageCapture 拍照(静态图片) 拍照按钮、连拍

这里有个关键点:多个 UseCase 可以同时绑定到同一个相机。比如你可以在预览的同时做图像分析,甚至同时录像。但要注意,不是所有组合都支持——比如某些低端机同时开 Preview + ImageAnalysis + VideoCapture 可能会卡顿。

我的经验:如果遇到组合不兼容,CameraX 会抛出异常。建议在初始化时用 CameraX.getAvailableUseCaseCombinations() 先查一下支持哪些组合。

三、生命周期绑定:再也不用担心泄漏了

以前用 Camera2,最头疼的就是生命周期管理。我记得有一次线上 bug,用户快速旋转屏幕导致相机崩溃,查了两天才发现是生命周期没处理好。

CameraX 的做法很聪明:它直接绑定到 LifecycleOwner。你只需要把 Activity 或 Fragment 传进去,剩下的打开、关闭、释放都由 CameraX 自动处理。

// 核心代码就这么几行
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)

cameraProviderFuture.addListener(Runnable {
    val cameraProvider = cameraProviderFuture.get()

    // 创建 Preview UseCase
    val preview = Preview.Builder().build()
    preview.setSurfaceProvider(previewView.surfaceProvider)

    // 绑定到生命周期
    val camera = cameraProvider.bindToLifecycle(
        lifecycleOwner,    // 通常是 Activity 或 Fragment
        CameraSelector.DEFAULT_BACK_CAMERA,
        preview
    )
}, ContextCompat.getMainExecutor(context))

看到了吗?bindToLifecycle 就是关键。当 Activity 走到 onDestroy 时,CameraX 会自动释放相机资源。你再也不用写那些又臭又长的 camera.open()camera.release() 了。

注意bindToLifecycle 必须在主线程调用。我曾经在子线程里调过,结果直接 crash 了。嗯,这个坑我替你们踩过了。

四、Preview:最简单的预览实现

Preview UseCase 负责把相机画面显示到屏幕上。它需要配合 PreviewView 使用——这是一个自定义 View,内部封装了 SurfaceView 和 TextureView 的切换逻辑。

// 在布局中
<androidx.camera.view.PreviewView
    android:id="@+id/previewView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

// 在代码中
val preview = Preview.Builder()
    .setTargetResolution(Size(1920, 1080))
    .build()

preview.setSurfaceProvider(previewView.surfaceProvider)

这里有个细节:setTargetResolution 不是强制分辨率,而是「建议分辨率」。CameraX 会选一个最接近的、设备支持的分辨率。我建议你设一个合理的值,别设 4K——很多低端机扛不住。

五、ImageAnalysis:逐帧分析的妙用

ImageAnalysis 是我个人用得最多的 UseCase。它可以把每一帧图像数据回调给你,让你做自定义处理。

val imageAnalysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()

imageAnalysis.setAnalyzer(executor, { imageProxy ->
    // imageProxy 就是当前帧的数据
    val buffer = imageProxy.planes[0].buffer
    // 在这里做你的图像处理逻辑
    imageProxy.close()  // 记得关闭!
})

关键点setBackpressureStrategy 有两种模式:STRATEGY_KEEP_ONLY_LATEST(只保留最新帧)和 STRATEGY_BLOCK_PRODUCER(阻塞直到处理完)。我建议用前者,否则处理慢的时候画面会卡顿。

另外,imageProxy.close() 一定要调。我曾经忘了调,结果内存泄漏,帧率越来越低。嗯,这个教训很深刻。

六、VideoCapture:录制视频的坑与解

VideoCapture 是相对较晚加入的 UseCase。它的用法和 Preview 类似,但多了个 VideoOutput 接口。

val videoCapture = VideoCapture.Builder()
    .setBitRate(10_000_000)  // 10 Mbps
    .build()

// 开始录制
videoCapture.startRecording(
    outputFileOptions,
    ContextCompat.getMainExecutor(context),
    object : VideoCapture.OnVideoSavedCallback {
        override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
            // 录制成功
        }

        override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
            // 录制失败
        }
    }
)

// 停止录制
videoCapture.stopRecording()

避坑指南:VideoCapture 和 ImageAnalysis 同时使用时,某些设备会报错。我建议先测试目标机型,或者用 CameraX.getAvailableUseCaseCombinations() 做兼容检查。

七、整体架构图

下面这张图展示了 CameraX 的核心架构。你可以看到,CameraX 位于应用层和 Camera2 之间,它屏蔽了底层的复杂性,向上提供简洁的 UseCase 接口。

CameraX 核心架构图 应用层(Activity / Fragment) UseCase 层 Preview ImageAnalysis VideoCapture ImageCapture CameraX 核心(生命周期绑定 + 设备兼容) Camera2 API(底层硬件抽象) 相机硬件(Camera HAL)

从这张图可以清楚看到:应用层只跟 UseCase 打交道,UseCase 层把请求交给 CameraX 核心,核心再通过 Camera2 跟硬件通信。生命周期绑定和兼容性处理都在 CameraX 核心层完成,对开发者完全透明。

八、总结

CameraX 的设计哲学,说白了就是「把复杂留给自己,把简单留给开发者」。它用 UseCase 模型把相机功能拆解成独立模块,用生命周期绑定解决了最头疼的资源管理问题。

我个人建议:新项目直接上 CameraX,除非你有非常特殊的定制需求(比如 RAW 输出、高速连拍)。Camera2 虽然灵活,但维护成本太高了。你想想看,为了兼容几百款机型,Google 在 CameraX 里投入了多少人力?我们直接用现成的就好。

最后送大家一句话:能用 CameraX 解决的问题,就别自己造轮子。把精力放在业务逻辑上,它不香吗?


公众号:蓝海资料掘金营,微信deep3321