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 内部通过 Lifecycle 回调自动管理相机打开/关闭;Compose 通过 DisposableEffect 确保清理。预览流使用 PreviewView 的 SurfaceProvider 或自定义 Surface,避免重复创建。
CameraX + Compose 协同架构 Compose 层 • @Composable 预览容器 • State(缩放/闪光灯) • AndroidView 桥接 • DisposableEffect 桥接层 LifecycleOwner SurfaceProvider State→UseCase DisposableEffect (remember/callback) CameraX 层 • ProcessCameraProvider • Preview UseCase • ImageAnalysis • CameraControl • Lifecycle 自动管理 双向生命周期同步

1.2 实现流程

实现 CameraX 与 Compose 协同遵循以下步骤:

  1. 添加依赖:在 build.gradle 中引入 CameraX 与 Compose 相关库。
  2. 创建 CameraX 管理器:在 ViewModel 或 Composable 内获取 ProcessCameraProvider。
  3. 绑定生命周期:将 LocalLifecycleOwner 传入 cameraProvider.bindToLifecycle()。
  4. 桥接预览 Surface:通过 AndroidView 创建 PreviewView 并设置 surfaceProvider。
  5. 状态同步:使用 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 日志: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()