.NET 9 MAUI自定义控件集成MLKit时CameraPreview无法渲染
解决.NET 9 MAUI中基于Google ML Kit的相机预览超时问题
因Zxing.MAUI无法识别特定QR码,在.NET 9 MAUI中基于Google ML Kit for Android实现二维码扫描功能。由于Camera.MAUI不支持.NET 9,自定义了QRCodeReaderView控件,但相机预览无法正常显示,Logcat报错:
Unable to configure camera Camera@461d7c2[id=0]
java.util.concurrent.TimeoutException: Future[androidx.camera.core.impl.utils.futures.ListFuture@4c0973e] is not done within 5000 ms.
尝试设置android:hardwareAccelerated="true"无效,以下是修复方案:
核心问题分析
错误根源是在主线程同步调用cameraProviderFuture.Get(),阻塞了CameraX的初始化流程,导致超时。CameraX要求异步处理相机提供者的初始化,不能同步阻塞主线程。
修复步骤
1. 修正ProcessCameraProvider初始化逻辑
移除同步Get()调用,将所有相机初始化逻辑放在AddListener回调中:
public void Connect(IMauiContext mauiContext, PreviewView previewView) { try { if (mauiContext.Context is null) { return; } cameraExecutor = Executors.NewSingleThreadExecutor(); var cameraProviderFuture = ProcessCameraProvider.GetInstance(mauiContext.Context); // 所有相机初始化逻辑移至回调内,避免主线程阻塞 cameraProviderFuture.AddListener(new Runnable(() => { try { var cameraProvider = cameraProviderFuture.Get() as ProcessCameraProvider; if (cameraProvider is null) return; var resolutionSelector = new ResolutionSelector.Builder() .SetResolutionStrategy( new ResolutionStrategy( boundSize: new Size(1280, 700), fallbackRule: ResolutionStrategy.FallbackRuleClosestHigherThenLower)) .Build(); _cameraPreview = new Preview.Builder() .SetResolutionSelector(resolutionSelector) .Build(); _cameraPreview.SetSurfaceProvider(previewView.SurfaceProvider); var imageAnalyzer = new ImageAnalysis.Builder() .SetBackpressureStrategy(ImageAnalysis.StrategyKeepOnlyLatest) .SetResolutionSelector(resolutionSelector) .Build(); var mLkitQrCodeAnalyzer = new MLkitQRCodeAnalyzer(); imageAnalyzer.SetAnalyzer(cameraExecutor, mLkitQrCodeAnalyzer); cameraProvider.UnbindAll(); var cameraSelector = CameraSelector.DefaultBackCamera; var hasCamera = cameraProvider.HasCamera(cameraSelector); Debug.WriteLine($"hasCamera: {hasCamera}"); // 优先使用当前Activity作为生命周期所有者 ILifecycleOwner? lifecycleOwner = Platform.CurrentActivity as ILifecycleOwner; lifecycleOwner ??= previewView.Context as ILifecycleOwner; if (lifecycleOwner != null) { _camera = cameraProvider.BindToLifecycle(lifecycleOwner, cameraSelector, _cameraPreview, imageAnalyzer); } } catch (Exception ex) { Debug.WriteLine($"Camera init error: {ex.Message}\n{ex.StackTrace}"); } }), ContextCompat.GetMainExecutor(mauiContext.Context)); } catch (Exception ex) { Debug.WriteLine($"Connect error: {ex.Message}\n{ex.StackTrace}"); } }
2. 完善Android平台视图实现
确保自定义的Android视图正确包裹PreviewView:
using Android.Content; using Android.Views; using AndroidX.Camera.View; namespace QRCodeScanner; public class AndroidQRCodeReaderView : FrameLayout { public PreviewView PreviewView { get; } public AndroidQRCodeReaderView(Context context, PreviewView previewView) : base(context) { PreviewView = previewView; LayoutParameters = new LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent); AddView(PreviewView); } }
3. 添加资源释放逻辑
在Handler的DisconnectHandler中释放相机资源,避免内存泄漏:
protected override void DisconnectHandler(AndroidQRCodeReaderView platformView) { base.DisconnectHandler(platformView); _camera?.Close(); cameraExecutor?.ShutdownNow(); _cameraPreview = null; _camera = null; }
4. 确保权限获取后再初始化相机
调整页面逻辑,权限授予后再触发相机初始化:
- 自定义控件添加初始化方法:
public class QRCodeReaderView : View { public void InitCamera() { if (Handler is QRCodeReaderViewHandler handler && handler.PlatformView is AndroidQRCodeReaderView platformView) { handler.Connect(MauiContext!, platformView.PreviewView); } } }
- 页面中调用:
protected override async void OnAppearing() { base.OnAppearing(); var status = await CheckAndRequestCameraPermission(); if (status == PermissionStatus.Granted) { qrCodeReaderView.InitCamera(); } }
5. 调整分辨率策略(可选)
若指定分辨率不被设备支持,可改为自动适配:
// 移除自定义分辨率选择器,使用CameraX默认配置 _cameraPreview = new Preview.Builder().Build(); imageAnalyzer = new ImageAnalysis.Builder() .SetBackpressureStrategy(ImageAnalysis.StrategyKeepOnlyLatest) .Build();
验证修复
完成修改后重新运行:
- 确认相机权限正常授予
- 检查Logcat无超时错误
- 验证相机预览正常显示
- 测试QR码扫描功能
内容的提问来源于stack exchange,提问作者Morse
相关产品推荐
相关产品推荐

