CameraX 核心概念与生命周期绑定
CameraX 是 Jetpack 家族中专为相机功能设计的库,它通过 用例(UseCase) 抽象了相机常见的操作场景。本章将深入理解 Preview、ImageCapture、ImageAnalysis 三个核心用例,并掌握如何将它们绑定到 LifecycleOwner,从而实现自动化生命周期管理。
3.1 三大核心用例概览
| 用例 | 职责 | 典型输出 | 生命周期行为 |
|---|---|---|---|
| Preview | 提供实时取景画面,通常与 PreviewView 配合 | Surface / Texture | Resume 时自动打开相机,Pause 时释放 |
| ImageCapture | 单张照片拍摄,支持闪关灯、对焦、EXIF | 文件 / Bitmap / MediaStore | 拍完自动释放,状态无关 |
| ImageAnalysis | 连续帧图像分析,用于二维码扫描、人脸检测等 | ImageProxy | 可配置背压策略(最新帧或所有帧) |
三个用例可以同时绑定到同一个 LifecycleOwner,CameraX 会按照以下顺序依次激活:Preview → ImageCapture → ImageAnalysis。绑定后您无需手动管理相机启动/停止,LifecycleOwner 的 resume/pause 事件会自动传递给 CameraX。
3.2 生命周期绑定机制
CameraX 的核心思想是 声明式绑定:您不需要自己调用 camera.open() / camera.close(),而是将用例声明给 ProcessCameraProvider,并让它与 LifecycleOwner 关联。示意图如下:
3.3 实现流程
- 添加依赖 – 在
build.gradle引入 CameraX 库,注意版本对齐。 - 声明权限 –
CAMERA权限,Android 10+ 可选录音权限。 - 获取 ProcessCameraProvider – 通过
ProcessCameraProvider.getInstance(context)异步获取。 - 创建用例 – 分别构造 Preview、ImageCapture、ImageAnalysis 并配置参数。
- 绑定到 LifecycleOwner – 调用
provider.bindToLifecycle(lifecycleOwner, cameraSelector, useCases)。 - 获取 Camera 控制权 – 绑定成功后返回
Camera对象,可调整 zoom、torch 等。
3.4 代码实战:Jetpack Compose 中绑定
在 Compose 中通过 AndroidView 嵌入 PreviewView,并在 DisposableEffect 中完成绑定。核心代码片段:
@Composable
fun CameraPreview(
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var camera by remember { mutableStateOf<Camera?>(null) }
AndroidView(
factory = { ctx ->
val previewView = PreviewView(ctx)
val providerFuture = ProcessCameraProvider.getInstance(ctx)
providerFuture.addListener({
val provider = providerFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
// 选择后置摄像头
val selector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 解绑现有用例
provider.unbindAll()
camera = provider.bindToLifecycle(
lifecycleOwner, selector, preview, imageCapture, imageAnalysis
)
} catch (e: Exception) {
Log.e("CameraX", "绑定失败", e)
}
}, ContextCompat.getMainExecutor(ctx))
previewView
},
modifier = modifier
)
}
关键点:
unbindAll()确保 View 复用前解绑旧用例,避免泄漏。- ImageAnalysis 的
STRATEGY_KEEP_ONLY_LATEST在帧处理慢时会自动丢弃旧的,避免队列堆积。 - 所有创建和绑定操作必须在主线程执行,使用
ContextCompat.getMainExecutor。
3.5 调试与部署注意事项
| 检查项 | 说明 |
|---|---|
| AndroidManifest 权限 | <uses-permission android:name="android.permission.CAMERA"/>,若需录音加上 RECORD_AUDIO |
| 相机硬件需求 | 在 AndroidManifest 声明 <uses-feature android:name="android.hardware.camera" android:required="false"/> 避免 Google Play 过滤 |
| 设备兼容性测试 | CameraX 1.0+ 覆盖 Android 5.0 以上 99% 设备,但部分低端机可能不支持同时绑定三个用例 |
| ProGuard 规则 | 如果开启混淆,需保留 CameraX 相关类:-keep class androidx.camera.** { *; } |
调试技巧:使用 adb shell dumpsys media.camera 查看相机状态;CameraX 1.2+ 提供了 CameraXConfig 设置日志级别。
3.6 常见错误与解决方案
-
CameraInfoUnavailableException– 多发生在模拟器或没有摄像头的设备。解决方案:检测PackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA),并在 UI 提示用户。 -
绑定后预览未显示 – 常见原因:
PreviewView未设置surfaceProvider,或bindToLifecycle传入的lifecycleOwner生命周期已销毁。确保在DisposableEffect中绑定,并在离开时调用provider.unbindAll()。 -
ImageCapture 无法拍照 – 检查是否给
ImageCapture传递了OutputFileOptions,并且文件存储目录有写入权限。 -
ImageAnalysis 收到空帧 – 通常是分辨率不兼容,使用
setTargetResolution(Size(1280, 720))限制。
3.7 性能与稳定性优化
重要:同时绑定多个用例会增加 GPU 和内存负载。以下建议可提升稳定性:
- 选择合适的分辨率 – Preview 和 ImageAnalysis 使用相同尺寸可减少缩放开销。推荐 720p 或 1080p。
- ImageAnalysis 背压策略 – 始终使用
STRATEGY_KEEP_ONLY_LATEST,避免分析回调耗尽线程池。 - 异步处理分析结果 – 在
ImageAnalysis.Analyzer中不要阻塞主线程,使用Executors.newSingleThreadExecutor()处理帧。 - 及时释放资源 – 如果不再需要 ImageAnalysis,调用
provider.unbind(analysisUseCase)释放帧缓冲区。 - 复用 ProcessCameraProvider – 该对象全局单例,避免重复初始化。
3.8 总结
本章掌握了 CameraX 三大用例的职责与生命周期绑定原理。核心要点:
- 声明式绑定 使代码简洁,免去手动管理相机开关。
- 用例组合 可以自由搭配,但受设备硬件的约束。
- ImageAnalysis 的背压策略 是防止 ANR 的关键。
- 使用
AndroidView在 Compose 中集成,并注意DisposableEffect的生命周期匹配。
下一步将在第4章中实战预览画面显示与相机切换。