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

Android CameraX无法打开相机或拍照问题求助

CameraX相机无法打开、按钮无响应的问题排查与修复

问题描述

使用Kotlin基于Android CameraX开发相机应用,编写CameraActivity和对应布局后,编译运行到设备上时相机无法打开,拍照按钮、闪光灯切换按钮均无响应,取消按钮未实现逻辑,已在Manifest中配置相机相关权限。

核心问题与修复方案

1. 相机初始化逻辑异步处理错误(最关键)

bindCamera方法中存在严重异步逻辑错误:同步获取ProcessCameraProvider后,在异步回调中才初始化preview和imageCapture,但bindToLifecycle却在回调外部执行,导致绑定的是未设置SurfaceProvider的空preview,且imageCapture从未绑定到相机,最终相机无法预览,拍照/闪光灯按钮因imageCapture为null无响应。

修复后的bindCamera方法:

private fun bindCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    cameraProviderFuture.addListener({
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
        cameraProvider.unbindAll()

        // 初始化Preview并绑定SurfaceProvider
        val preview = Preview.Builder()
            .setTargetResolution(Size(resources.displayMetrics.widthPixels, resources.displayMetrics.heightPixels))
            .build()
            .apply {
                setSurfaceProvider(ui.viewFinder.createSurfaceProvider(null))
            }

        // 初始化ImageCapture
        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .setTargetRotation(windowManager.defaultDisplay.rotation)
            .setFlashMode(flashMode)
            .build()

        // 绑定所有Use Case到生命周期
        try {
            cameraProvider.bindToLifecycle(
                this,
                CameraSelector.DEFAULT_BACK_CAMERA,
                preview,
                imageCapture
            )
        } catch (exc: Exception) {
            logger.error("Use case binding failed: $exc")
        }
    }, ContextCompat.getMainExecutor(this))
}

2. 权限检查与请求逻辑不一致

hasNoPermissions方法检查了相机、读/写存储三个权限,但checkCameraPermissions只请求了相机权限。如果存储权限未授予,hasNoPermissions返回true,但不会触发存储权限请求,相机初始化逻辑永远不会执行。

修复方案:

  • 若不需要存储权限,直接从hasNoPermissions中移除读/写存储的检查;
  • 若需要存储权限,修改checkCameraPermissions请求所有必要权限:
fun checkCameraPermissions(context: Context?) {
    val requiredPermissions = arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
    )
    if (requiredPermissions.any {
            ContextCompat.checkSelfPermission(context!!, it) != PackageManager.PERMISSION_GRANTED
        }) {
        ActivityCompat.requestPermissions(this, requiredPermissions, 100)
    } else {
        // 权限已全部授予,直接初始化相机
        bindCamera()
    }
}

3. 布局中存在冲突的相机预览控件

布局文件同时包含CameraX的PreviewView和第三方的com.journeyapps.barcodescanner.CameraPreview,两个控件都会抢占相机资源,导致CameraX无法正常获取相机,且CameraPreview会遮挡PreviewView。

修复方案:
删除布局中的CameraPreview控件,只保留CameraX的PreviewView:

<!-- 移除以下代码 -->
<com.journeyapps.barcodescanner.CameraPreview
    android:id="@+id/camera_preview"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</com.journeyapps.barcodescanner.CameraPreview>

4. 权限授予后未处理已授权场景

onStart方法中,仅当hasNoPermissions为true时才触发权限检查,但如果权限已提前授予,不会调用bindCamera初始化相机。

修复方案:
修改onStart方法,权限已授予时直接初始化相机:

override fun onStart() {
    super.onStart()
    if (hasNoPermissions()) {
        checkCameraPermissions(applicationContext)
    } else {
        bindCamera()
    }
}

5. ImageCapture回调未释放资源(潜在问题)

takePicture使用OnImageCapturedCallback时,必须手动关闭ImageProxy,否则会导致相机资源泄漏,后续无法正常使用相机。

修复拍照回调:

ui.btTakePhoto.setOnClickListener {
    imageCapture?.takePicture(
        ContextCompat.getMainExecutor(this),
        object : ImageCapture.OnImageCapturedCallback() {
            override fun onCaptureSuccess(image: ImageProxy) {
                super.onCaptureSuccess(image)
                // 处理图片逻辑...
                // 必须关闭ImageProxy释放资源
                image.close()
            }

            override fun onError(exception: ImageCaptureException) {
                logger.error("Error taking picture: $exception")
            }
        }
    )
}

额外优化建议

  • 闪光灯按钮点击时,先判断imageCapture是否已初始化,避免空指针:
ui.btSwitchFlashlight.setOnClickListener {
    imageCapture?.let { capture ->
        capture.flashMode = if (capture.flashMode == ImageCapture.FLASH_MODE_ON) {
            ImageCapture.FLASH_MODE_OFF
        } else {
            ImageCapture.FLASH_MODE_ON
        }
    }
}
  • 权限请求结果处理中,确保requestCode匹配(当前代码中PermissionChecker.isGranted未明确判断requestCode,需确认逻辑正确性)。

内容的提问来源于stack exchange,提问作者Manuel Lucas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 14:07:04