如何在BottomSheet折叠时停止CameraX相机运行?
解决BottomSheet折叠时CameraX相机资源未释放的问题
核心问题在于CameraX默认绑定Activity生命周期,而BottomSheet的展开/折叠属于Composable组合生命周期范畴,两者不匹配导致折叠后相机仍在后台运行。以下是具体解决思路和实现代码:
关键思路
用DisposableEffect监听BottomSheet的状态变化,将相机的绑定/解绑逻辑与BottomSheet的展开状态强关联,替代原有的Activity生命周期绑定:
- 当BottomSheet展开时,初始化并绑定CameraX的Preview和ImageAnalysis UseCase
- 当BottomSheet折叠时,解绑所有UseCase并清空引用,彻底释放相机资源
- 利用
DisposableEffect的onDispose回调处理Composable退出组合时的资源清理
实现代码
@Composable fun BarcodeScannerBottomSheet() { val bottomSheetState = rememberBottomSheetState(BottomSheetValue.Collapsed) val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current // 持有CameraProvider及UseCase实例 var cameraProvider: ProcessCameraProvider? by remember { mutableStateOf(null) } var previewUseCase: Preview? by remember { mutableStateOf(null) } var imageAnalysisUseCase: ImageAnalysis? by remember { mutableStateOf(null) } // 初始化CameraProvider LaunchedEffect(Unit) { cameraProvider = ProcessCameraProvider.getInstance(context).await() } // 绑定BottomSheet状态与相机生命周期 DisposableEffect(bottomSheetState.currentValue, cameraProvider) { val provider = cameraProvider ?: return@DisposableEffect onDispose {} if (bottomSheetState.currentValue == BottomSheetValue.Expanded) { // 配置预览组件(替换成你的SurfaceView引用) previewUseCase = Preview.Builder().build().also { it.setSurfaceProvider(yourSurfaceView.surfaceProvider) } // 配置条码分析器 imageAnalysisUseCase = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build().also { it.setAnalyzer(Executors.newSingleThreadExecutor(), yourBarcodeAnalyzer) } // 绑定相机到当前LifecycleOwner(贴近Composable组合生命周期) try { provider.unbindAll() provider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, previewUseCase, imageAnalysisUseCase ) } catch (e: Exception) { Log.e("CameraX", "相机绑定失败", e) } } else { // 折叠时解绑所有UseCase,释放资源 provider.unbindAll() previewUseCase = null imageAnalysisUseCase = null } // Composable退出组合时彻底清理 onDispose { provider.unbindAll() cameraProvider = null } } // 你的BottomSheetScaffold实现 BottomSheetScaffold( sheetState = bottomSheetState, sheetContent = { // 相机预览布局(包含你的SurfaceView) AndroidView( factory = { ctx -> SurfaceView(ctx).apply { id = R.id.camera_preview } }, update = { view -> yourSurfaceView = view } ) } ) { // 主内容区 } }
注意事项
- 必须调用
cameraProvider.unbindAll()才能彻底停止相机,仅调用imageAnalysis.clearAnalyzer()只会移除分析器,相机实例仍会保持活跃 - 绑定相机时使用
LocalLifecycleOwner.current而非Activity的LifecycleOwner,让相机生命周期更贴近Composable的组合生命周期 - 所有CameraX相关实例需用
remember持有,避免重组时重复初始化
内容的提问来源于stack exchange,提问作者devMohaned
相关产品推荐
相关产品推荐

