You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,提问作者رضا پریور

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:50:11