Flutter中的异常处理
说明
FLutter
中的错误不会导致应用程序奔溃,只会终止执行出错代码之后的逻辑,在导致Widget.build()
返回为null
的错误会导致Widget
构建失败,并返回红底黄字的错误原因Widget
(在Release模式则会显示为灰底区域),对于异步方法产生异常等Flutter框架
没有捕获的情况,会交由当前代码所在的Zone
处理。
为什么 flutter 触发异常的时候不会崩溃?
这个和flutter
的消息循环机制有关,任务分两种,一个是微任务microtask
,一个是事件event
,他们有自己的队列,每个任务是相互独立的,一旦某个任务触发异常,也就是导致这个任务后续代码无法执行,并不会影响其他任务执行
本文基于Flutter (Channel stable, 2.2.3)
详细说明
Flutter中的错误处理分为以下几种:
try...catch
对于普通的错误,可以通过try...catch
来捕获:
1 | try { |
对于,异步异常,可以使用await
等待其执行完毕,将其变为同步任务,否则无法则捕获。
ErrorWidget.builder
当在Widget
构建过程中出现错误,导致build()
方法返回null
,Flutter框架
会调用ErrorWidget.builder
返回一个Widget
替代出错的Widget
。
默认情况下,debug
模式返回的是红底黄字的错误提示,而release
模式返回的是灰色Widget
。
可以在RunApp
方法中替换这个默认的错误界面:
1 | runApp(MaterialApp( |
上述代码中的MainErrorWidget
是一个自定义的展示错误信息的页面。
MainErrorWidget
的一种实现方式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class 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;
}
}
需要注意的是,错误Widget
在Debug
和Release
模式下有一些区别:
Debug
模式下ErrorWidget.builder
会返回错误详细信息FlutterErrorDetails
,Release
下则不会;Debug
模式下,Widget
等出错会打印Exception caught by widgets library ...
等提示并输出错误堆栈信息,但是Release
模式下不会;Debug
模式下,出错不会调用FlutterError.onError
,Release
模式下会。
FlutterError.onError
上述几种情况都没有处理的,被Flutter框架引起的异常,会在这里被处理。
在Flutter 2.2.3
中,Debug
模式下如onPressed
中的未捕获错误等都会被Widget等捕获,而不会走到这里来,在Release
模式下则会调用FlutterError.onError
。
在这里可以对错误进行处理,比如输出到控制台、交给Zone统一处理、直接结束掉APP等:
FlutterError.dumpErrorToConsole(details);
输出到控制台exit(1);
退出APPZone.current.handleUncaughtError(details.exception, details.stack);
交给Zone统一处理defaultOnError?.call(details);
自己处理完异常后,也要把异常向上抛 【推荐】,其中defaultOnError
可以预先缓存final defaultOnError = FlutterError.onError;
runZonedGuarded(onError)
上述几种情况都没有处理的异常,会被发送到这里处理,可以类比为Android
中的Thread.UncaughtExceptionHandler
。
1 | runZonedGuarded(() async { |
Zone可以理解为一个沙盒,其中的代码出错,包括异步的都可以捕获到。但是如果是另外一个沙盒中的错误则无法处理。