跳至主要內容

Flutter 中的异常处理

JI,XIAOYONG...大约 4 分钟flutter

说明

FLutter中的错误不会导致应用程序奔溃,只会终止执行出错代码之后的逻辑,在导致Widget.build()返回为null的错误会导致Widget构建失败,并返回红底黄字的错误原因Widget(在 Release 模式则会显示为灰底区域);一般来说,Flutter 中的错误都会被FlutterError.onError捕获并处理;对于异步方法产生异常等Flutter框架没有捕获的情况,会交由当前代码所在的Zone处理(这些异常可以使用runZonedGuarded捕获并处理)。

为什么 flutter 触发异常的时候不会崩溃?
这个和 flutter 的消息循环机制有关,任务分两种,一个是微任务 microtask,一个是事件 event,他们有自己的队列,每个任务是相互独立的,一旦某个任务触发异常,也就是导致这个任务后续代码无法执行,并不会影响其他任务执行

本文基于 Flutter (Channel stable, 2.2.3)

详细说明

Flutter 中的错误处理分为以下几种:

try...catch

对于普通的错误,可以通过try...catch来捕获:

               try {
                  var list = [1, 2];
                  var three = list[3];
                } on RangeError catch (e) {
                  print("这里是捕获 RangeError 类型的异常 $e");
                } catch (e) {
                  print("这里是兜底的捕获异常 $e");
                } finally {
                  print("这里是无论如何都会执行的代码");
                }

对于,异步异常,可以使用await等待其执行完毕,将其变为同步任务,否则无法则捕获。

ErrorWidget.builder

当在Widget构建过程中出现错误,导致build()方法返回nullFlutter框架会调用ErrorWidget.builder返回一个Widget替代出错的Widget

默认情况下,debug模式返回的是红底黄字的错误提示,而release模式返回的是灰色Widget

可以在RunApp方法中替换这个默认的错误界面:

runApp(MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: BodyWidget(),
      ),
      builder: (context, widget) {
        // Widget 在 Build 时出错的话,展示此 Widget,
        // 如果不定义的话,debug 下为红底黄字错误信息,release 会显示为灰色布局
        // errorDetails 在 release 模式下为空
        ErrorWidget.builder = (FlutterErrorDetails errorDetails) {
          return MainErrorWidget(widget, errorDetails);
        };
        return widget ?? Container();
      },
    ));

上述代码中的MainErrorWidget是一个自定义的展示错误信息的页面。

  • MainErrorWidget的一种实现方式

    class MainErrorWidget extends StatelessWidget {
      Widget? parentWidget;
    
      FlutterErrorDetails errorDetails;
    
      MainErrorWidget(this.parentWidget, this.errorDetails);
    
      
      Widget build(BuildContext context) {
        print("3. 布局出现错误,展示错误页面,此处错误在release中也会调用FlutterError.onError");
    
        Widget error = Card(
          child: SingleChildScrollView(
            child: Container(
              padding: const EdgeInsets.all(20),
              color: Colors.green,
              child: Text(
                '布局出现错误,以下是错误信息:\n$errorDetails',
                style: TextStyle(fontSize: 10, color: Colors.white),
              ),
            ),
          ),
        );
        if (parentWidget is Scaffold || parentWidget is Navigator) {
          debugPrint(
              "widget${parentWidget?.key?.toString()} ($parentWidget)  is Scaffold ${parentWidget is Scaffold} or Navigator ${parentWidget is Navigator}");
          // error = Container(child: error);
        }
        return error;
      }
    }
    

需要注意的是,错误WidgetDebugRelease模式下有一些区别:

  • Debug模式下ErrorWidget.builder会返回错误详细信息FlutterErrorDetailsRelease下则不会;
  • Debug模式下,Widget等出错会打印Exception caught by widgets library ... 等提示并输出错误堆栈信息,但是Release模式下不会;
  • Debug模式下,出错不会调用FlutterError.onErrorRelease模式下会。

FlutterError.onError

上述几种情况都没有处理的,被 Flutter 框架引起的异常,会在这里被处理。

Flutter 2.2.3中,Debug模式下如onPressed中的未捕获错误等都会被 Widget 等捕获,而不会走到这里来,在Release模式下则会调用FlutterError.onError

在这里可以对错误进行处理,比如输出到控制台、交给 Zone 统一处理、直接结束掉 APP 等:

  • FlutterError.dumpErrorToConsole(details); 输出到控制台
  • exit(1); 退出 APP
  • Zone.current.handleUncaughtError(details.exception, details.stack); 交给 Zone 统一处理
  • defaultOnError?.call(details); 自己处理完异常后,也要把异常向上抛【推荐】,其中defaultOnError 可以预先缓存final defaultOnError = FlutterError.onError;

runZonedGuarded(onError)

上述几种情况都没有处理的异常,会被发送到这里处理,可以类比为Android中的Thread.UncaughtExceptionHandler

runZonedGuarded(() async {
    runApp(...);
  },
  (Object error, StackTrace stack) {
    // 没有被 Flutter 捕获的错误,全局未捕获异常处理,类似于 Android 的 Thread.UncaughtExceptionHandler
    /// 比如异步的方法
    print("2. runZonedGuarded.onError $error");
  });

Zone 可以理解为一个沙盒,其中的代码出错,包括异步的都可以捕获到。但是如果是另外一个沙盒中的错误则无法处理。

参考文章

Flutter 官网异常处理open in new window

Flutter 异常处理open in new window

文章标题:《Flutter 中的异常处理》
本文作者: JI,XIAOYONG
发布时间: 2021/07/29 15:41:47 UTC+8
更新时间: 2023/12/30 16:17:02 UTC+8
written by human, not by AI
本文地址: https://jixiaoyong.github.io/blog/posts/9e235953.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。转载请注明出处!
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8