CameraX+Jetpack Compose:模拟器后置相机拍照报错问题排查
CameraX + Jetpack Compose 模拟器后置相机拍照报错问题
问题描述
我用CameraX结合Jetpack Compose开发相机功能,真机运行完全正常,但在Android Studio模拟器里调用后置相机拍照时会报错“Photo capture failed: Camera is closed”,切换前置相机就能正常工作。我测试了API 31和33版本,就算把拍照点击事件里的dismiss()方法去掉,问题还是存在,想确认这是代码问题还是模拟器本身的问题。
相关代码
@Composable fun CameraScreen(changeImage: (Uri) -> Unit,onDismiss: () -> Unit) { val context = LocalContext.current val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current var preview by remember { mutableStateOf<Preview?>(null) } var imageCapture: ImageCapture? = null var lensFacing by remember { mutableStateOf(CameraSelector.LENS_FACING_BACK) } var zoomRatio by remember { mutableStateOf(1.0f) } var camera by remember { mutableStateOf<Camera?>(null) } var previewView by remember { mutableStateOf<PreviewView>(PreviewView(context)) } LaunchedEffect(lensFacing) { val cameraProvider = context.getCameraProvider() try { cameraProvider.unbindAll() preview = Preview.Builder().build().also { it.setSurfaceProvider(previewView.surfaceProvider) } imageCapture = ImageCapture.Builder().build() val cameraSelector = CameraSelector.Builder() .requireLensFacing(lensFacing) .build() camera = cameraProvider.bindToLifecycle( lifecycleOwner, cameraSelector, preview, imageCapture ) } catch (exc: Exception) { Log.e("Camera", "Use case binding failed", exc) } } Box(modifier = Modifier.fillMaxSize()) { AndroidView( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { detectTransformGestures { _, _, zoom, _ -> // Adjust zoom ratio based on the pinch gesture val newZoom = max( 1f, min(zoomRatio * zoom, 5f) ) // Assuming max zoom is 5x, adjust as needed zoomRatio = newZoom camera?.cameraControl?.setZoomRatio(newZoom) } }, factory = { ctx -> PreviewView(ctx).also { previewView = it } } ) Row( modifier = Modifier .align(Alignment.BottomCenter) .fillMaxWidth() .padding(16.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { // Button to switch camera Button(onClick = { lensFacing = if (lensFacing == CameraSelector.LENS_FACING_BACK) CameraSelector.LENS_FACING_FRONT else CameraSelector.LENS_FACING_BACK }) { Text(text = "Switch Camera") } // Button to capture image Button(onClick = { val fileName = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(System.currentTimeMillis()) + ".jpg" val photoFile = File(context.filesDir, fileName) val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build() imageCapture?.takePicture( outputOptions, ContextCompat.getMainExecutor(context), object : ImageCapture.OnImageSavedCallback { override fun onError(exc: ImageCaptureException) { Log.e("Camera", "Photo capture failed: ${exc.message}", exc) } override fun onImageSaved(output: ImageCapture.OutputFileResults) { val savedUri = Uri.fromFile(photoFile) changeImage(savedUri) Log.d("Camera", "Photo capture succeeded: $savedUri") // Handle the saved image URI here onDismiss() } } ) }) { Text("Capture") } } }
问题分析与结论
这大概率是Android Studio模拟器的后置相机模拟缺陷,而非你的代码问题,理由如下:
- 真机运行完全正常,说明代码逻辑没有根本性错误
- 前置相机在模拟器可正常工作,仅后置相机拍照触发报错,符合模拟器对后置相机模拟的常见兼容性问题
- 移除
onDismiss()后问题依旧,排除了页面生命周期提前结束导致相机被关闭的可能
代码优化建议(提升稳定性)
虽然不是问题根源,但可以调整以下细节减少潜在风险:
- 将
imageCapture改为remember状态变量,避免每次重组都重新初始化:var imageCapture by remember { mutableStateOf<ImageCapture?>(null) } - 拍照前增加可用性检查,避免空指针或无效状态调用:
Button(onClick = { val capture = imageCapture ?: return@Button val cam = camera ?: return@Button // 后续拍照逻辑保持不变 }) - 替换已过时的
SimpleDateFormat,使用更安全的DateTimeFormatter:val fileName = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss", Locale.getDefault()) .format(LocalDateTime.now()) + ".jpg"
内容的提问来源于stack exchange,提问作者رضا پریور
相关产品推荐
相关产品推荐

