Jetpack Compose中CameraX绑定ImageAnalysis时出现Not bound to a valid camera错误
CameraX绑定ImageAnalysis报错「Not Bound To A valid camera: ImageCapture CameraX」解决方法
问题现象
- 使用CameraX图像分析功能检测人脸微笑、眨眼动作时,执行
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis)会触发「Not Bound To A valid camera: ImageCapture CameraX」错误 - 将绑定对象替换为
preview和imageCapture时,代码可正常运行
问题原因
- ImageAnalysis分辨率设置错误:代码中直接使用未完成布局的
previewView.width和previewView.height作为目标分辨率,此时宽高值为0,CameraX无法匹配到相机支持的输出规格,导致绑定失败。 - 用例绑定不完整:创建了
imageCapture实例但未将其绑定到生命周期,后续调用takePhoto时,未绑定的imageCapture会触发相机有效性校验错误。
解决方案
- 移除错误的分辨率设置:让CameraX自动适配相机支持的分辨率(推荐),或在
previewView布局完成后获取真实宽高再设置。 - 统一绑定所有用例:将
imageCapture加入bindToLifecycle的参数列表,确保所有相机用例都绑定到生命周期。
修改后的完整代码
@Composable fun FaceDetectorCameraView( hasPassedAllChecks: Boolean, onImageCaptured: (Uri) -> Unit, onErrorFace: (String) -> Unit, executor: Executor, doProcessFace: (List<Face>) -> Unit, onError: (ImageCaptureException) -> Unit ) { val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current val preview = Preview.Builder().build() val previewView = remember { PreviewView(context) } val imageCapture: ImageCapture = remember { ImageCapture.Builder().build() } val imageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setImageQueueDepth(10) .build() .apply { setAnalyzer(executor, FaceAnalyzer( object : FaceAnalyzerCallback{ override fun processFace(faces: List<Face>) { doProcessFace(faces) } override fun errorFace(error: String) { onErrorFace(error) } } )) } LaunchedEffect(cameraSelector) { val cameraProvider = context.getCameraProvider() // 绑定所有用到的相机用例 cameraProvider.bindToLifecycle( lifecycleOwner, cameraSelector, preview, imageAnalysis, imageCapture ) preview.setSurfaceProvider(previewView.surfaceProvider) } AndroidView( modifier = Modifier .fillMaxWidth() .requiredHeightIn(80.dp) .padding(start = 20.dp, end = 20.dp, top = 10.dp), factory = { ctx -> previewView }, ) if(hasPassedAllChecks){ LaunchedEffect(true){ delay(1500) takePhoto( contentResolver = context.contentResolver, imageCapture = imageCapture, executor = executor, onImageCaptured = onImageCaptured, onError = onError ) } } }
函数调用方式保持不变:
FaceDetectorCameraView( hasPassedAllChecks = hasPassedAllChecks, onImageCaptured = { onImageTaken(it) }, onErrorFace = {}, executor = ContextCompat.getMainExecutor(context), doProcessFace = doProcessFace, onError = { Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show() }, )
可选:指定真实分辨率的实现
如果必须自定义目标分辨率,可通过监听previewView布局完成事件获取真实宽高:
LaunchedEffect(previewView) { previewView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { previewView.viewTreeObserver.removeOnGlobalLayoutListener(this) val realSize = Size(previewView.width, previewView.height) val updatedImageAnalysis = ImageAnalysis.Builder() .setTargetResolution(realSize) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setImageQueueDepth(10) .build() .apply { setAnalyzer(executor, FaceAnalyzer( object : FaceAnalyzerCallback{ override fun processFace(faces: List<Face>) { doProcessFace(faces) } override fun errorFace(error: String) { onErrorFace(error) } } )) } // 重新绑定用例 val cameraProvider = context.getCameraProvider() cameraProvider.unbindAll() cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, updatedImageAnalysis, imageCapture) } }) }
内容的提问来源于stack exchange,提问作者ibramazin
相关产品推荐
相关产品推荐

