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

如何捕获Blazor MAUI Hybrid中所有未处理异常(含DisposeAsync异常)

Blazor MAUI Hybrid 全局未处理异常完整捕获方案

针对你遇到的ErrorBoundary无法捕获组件DisposeAsync异常的问题,以下是适配Blazor MAUI Hybrid的多层级捕获方案:

1. MAUI 平台级全局异常捕获

MAUI提供了跨平台及平台特定的异常捕获入口,能覆盖未被Blazor层处理的异步异常(包括DisposeAsync抛出的异常):

  • 在App.xaml.cs中注册全局事件:
public App()
{
    InitializeComponent();

    // 捕获AppDomain未处理异常
    AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
    {
        var ex = args.ExceptionObject as Exception;
        UploadCrashLog(ex); // 替换为你的崩溃日志上传逻辑
    };

    // 捕获未被观察到的Task异常(异步操作最常见的漏捕场景)
    TaskScheduler.UnobservedTaskException += (sender, args) =>
    {
        UploadCrashLog(args.Exception);
        args.SetObserved(); // 标记为已处理,避免触发系统崩溃提示
    };
}
  • 平台补充捕获:
    • Android:在MainActivity.cs的OnCreate方法中添加:
      AndroidEnvironment.UnhandledExceptionRaiser += (sender, args) =>
      {
          UploadCrashLog(args.Exception);
          args.Handled = true;
      };
      
    • iOS:在AppDelegate.cs的FinishedLaunching方法中添加:
      NSExceptionHandler.DefaultExceptionHandler = (exception) =>
      {
          UploadCrashLog(new Exception(exception.Reason, new System.Exception(exception.CallStackSymbols)));
          return true;
      };
      

2. Blazor 组件异步释放异常兜底

为避免组件DisposeAsync直接抛出异常,创建通用安全基类统一处理:

public class SafeDisposableComponent : ComponentBase, IAsyncDisposable
{
    public async ValueTask DisposeAsync()
    {
        try
        {
            await OnDisposeAsync();
        }
        catch (Exception ex)
        {
            UploadCrashLog(ex);
        }
        finally
        {
            GC.SuppressFinalize(this);
        }
    }

    // 让业务组件重写这个方法实现异步释放逻辑
    protected virtual ValueTask OnDisposeAsync() => ValueTask.CompletedTask;
}

所有需要异步释放的组件继承该基类,重写OnDisposeAsync而非直接实现IAsyncDisposable,确保异常被拦截。

3. 增强 Blazor ErrorBoundary 覆盖范围

自定义ErrorBoundary扩展默认逻辑,确保组件树内的同步/异步异常都被捕获:

public class GlobalErrorBoundary : ErrorBoundary
{
    protected override Task OnErrorAsync(Exception exception)
    {
        UploadCrashLog(exception);
        return base.OnErrorAsync(exception);
    }
}

然后用这个自定义边界包裹根组件(如Main.razor):

<GlobalErrorBoundary>
    <ChildContent>
        <!-- 你的应用根内容 -->
    </ChildContent>
    <ErrorContent>
        <p>应用出现异常,已记录日志</p>
    </ErrorContent>
</GlobalErrorBoundary>

4. 日志系统兜底捕获

配置自定义日志提供者,捕获所有日志中的异常信息:

public class CrashLogProvider : ILoggerProvider
{
    public ILogger CreateLogger(string categoryName) => new CrashLogger();

    public void Dispose() { }

    private class CrashLogger : ILogger
    {
        public IDisposable? BeginScope<TState>(TState state) => null;

        public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Error;

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
        {
            if (exception != null)
            {
                UploadCrashLog(exception);
            }
        }
    }
}

在MauiProgram.cs中注册该提供者:

builder.Logging.AddProvider(new CrashLogProvider());

验证方案有效性

用你提到的测试方法验证:创建继承SafeDisposableComponent的组件,在OnDisposeAsync中抛出异常,检查是否被捕获上传,且不会触发系统默认崩溃提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:45:15