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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:08:11