1. CameraX与Compose的协同架构:生命周期绑定、预览流传递、状态同步
1.1 架构原理
CameraX 是 Jetpack 相机库,通过 ProcessCameraProvider 与 Android 生命周期绑定;Compose 是声明式 UI 框架,两者协同需要解决三个核心问题:
- 生命周期绑定:CameraX 的
LifecycleOwner必须与 Compose 的LocalLifecycleOwner保持一致,确保相机在正确的生命周期阶段启动/释放。 - 预览流传递:CameraX 的
Preview.SurfaceProvider需要通过AndroidView桥接到 Compose 的Surface或TextureView。 - 状态同步:相机参数(如闪光灯、缩放)以 Compose 状态驱动,通过
remember/mutableStateOf触发 CameraX 重新配置。
📌 架构要点
CameraX 内部通过
CameraX 内部通过
Lifecycle 回调自动管理相机打开/关闭;Compose 通过 DisposableEffect 确保清理。预览流使用 PreviewView 的 SurfaceProvider 或自定义 Surface,避免重复创建。
1.2 实现流程
实现 CameraX 与 Compose 协同遵循以下步骤:
- 添加依赖:在
build.gradle中引入 CameraX 与 Compose 相关库。 - 创建 CameraX 管理器:在 ViewModel 或 Composable 内获取
ProcessCameraProvider。 - 绑定生命周期:将
LocalLifecycleOwner传入cameraProvider.bindToLifecycle()。 - 桥接预览 Surface:通过
AndroidView创建PreviewView并设置surfaceProvider。 - 状态同步:使用
remember保存相机状态,通过回调更新 UseCase 参数。
1.3 关键代码与配置
1.3.1 添加 Gradle 依赖
// build.gradle (Module: app)
dependencies {
// CameraX
implementation "androidx.camera:camera-core:1.4.0"
implementation "androidx.camera:camera-camera2:1.4.0"
implementation "androidx.camera:camera-lifecycle:1.4.0"
implementation "androidx.camera:camera-view:1.4.0"
// Compose
implementation "androidx.compose.ui:ui:1.6.0"
implementation "androidx.compose.foundation:foundation:1.6.0"
implementation "androidx.compose.runtime:runtime:1.6.0"
implementation "androidx.activity:activity-compose:1.8.0"
}
1.3.2 核心 Composable 实现
@Composable
fun CameraPreview(
modifier: Modifier = Modifier,
lensFacing: Int = CameraSelector.LENS_FACING_BACK
) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
var cameraProvider by remember { mutableStateOf<ProcessCameraProvider?>(null) }
// 初始化 CameraProvider
LaunchedEffect(Unit) {
val provider = context.getCameraProvider()
cameraProvider = provider
}
// 通过 AndroidView 桥接 PreviewView
AndroidView(
factory = { ctx ->
PreviewView(ctx).apply {
scaleType = PreviewView.ScaleType.FILL_CENTER
}
},
modifier = modifier
)
// 绑定生命周期和 UseCase
DisposableEffect(lifecycleOwner, cameraProvider) {
val provider = cameraProvider ?: return@DisposableEffect onDispose {}
val preview = Preview.Builder().build()
val selector = CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build()
val surfaceProvider = /* 从 AndroidView 获取 */ // 详见下方完整示例
try {
provider.unbindAll()
provider.bindToLifecycle(
lifecycleOwner,
selector,
preview
)
} catch (e: Exception) {
Log.e("CameraX", "绑定失败", e)
}
onDispose {
provider.unbindAll()
}
}
}
// 扩展函数:获取 CameraProvider
suspend fun Context.getCameraProvider(): ProcessCameraProvider =
ProcessCameraProvider.getInstance(this).await()
1.3.3 完整桥接:SurfaceProvider 传递
需要将 PreviewView 的 surfaceProvider 传递到 CameraX 的 Preview UseCase。正确做法:在 AndroidView 的 update 中获取 surfaceProvider 并保存在 remember 中。
@Composable
fun CameraScreen(modifier: Modifier = Modifier) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
var previewView by remember { mutableStateOf<PreviewView?>(null) }
AndroidView(
factory = { ctx ->
PreviewView(ctx).also { view ->
previewView = view
}
},
modifier = modifier
)
val surfaceProvider = remember(previewView) {
previewView?.surfaceProvider
}
LaunchedEffect(surfaceProvider, lifecycleOwner) {
val provider = context.getCameraProvider()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(surfaceProvider)
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
provider.unbindAll()
provider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview
)
} catch (e: Exception) {
Log.e("CameraX", "绑定失败", e)
}
}
}
1.4 调试与常见错误
| 错误现象 | 可能原因 | 调试方法 |
|---|---|---|
| 预览黑屏 | SurfaceProvider 未设置或生命周期未绑定 | 检查 setSurfaceProvider 是否调用;确认 lifecycleOwner 处于 STARTED 以上 |
| 崩溃:CameraX 已关闭 | 重复调用 bindToLifecycle 未先 unbindAll |
在 LaunchedEffect 中先 provider.unbindAll() |
| 状态不同步(闪光灯无效) | 状态变化后未重启 UseCase | 使用 camera.cameraControl 直接设置,或重新 bind |
| 预览闪烁或尺寸异常 | PreviewView scaleType 不匹配 | 设置 scaleType = FILL_CENTER 或 FIT_CENTER |
📘 调试技巧
启用 CameraX 日志:
启用 CameraX 日志:
adb shell setprop log.tag.CameraX DEBUG,查看 adb logcat -s CameraX 输出生命周期绑定和 Surface 创建日志。
1.5 性能与稳定性优化
- 避免频繁重组:将
cameraProvider和surfaceProvider使用remember或rememberSaveable缓存,防止每次重组时重新创建。 - 控制 UseCase 数量:不需要时不要同时绑定
ImageAnalysis和ImageCapture,减少内存和带宽开销。 - 及时释放资源:在
DisposableEffect的onDispose中调用provider.unbindAll()和释放监听器。 - 使用
ProcessCameraProvider单例:同一进程内多次获取getInstance()返回相同对象,避免重复初始化。
1.6 结构总结
下表对比了三种协同核心机制的关键实现点:
| 协同机制 | 实现手段 | 关键 API |
|---|---|---|
| 生命周期绑定 | 传递 LocalLifecycleOwner 给 bindToLifecycle |
ProcessCameraProvider.bindToLifecycle() |
| 预览流传递 | 通过 AndroidView 获得 PreviewView.surfaceProvider |
Preview.setSurfaceProvider() |
| 状态同步 | Compose 状态驱动 camera.cameraControl 或重新 bind |
CameraControl.* / UseCase.rebind() |