Android 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

