我正在参与「启航方案」
Flutter怎么捕获反常
- 程序反常会怎样
- 在Java中,程序产生反常且没有被捕获,那么程序将会停止。
- 但在Dart或JavaScript中则不会,究其原因,这和它们的运行机制有关系,Java是多线程模型的编程语言,恣意一个线程触发反常且没被捕获时,整个进程就退出。
- 但Dart和JavaScript不会,它们都是单线程模型,运行机制很类似(但有差异)。
- flutter怎么捕获反常
- Dart中能够经过
try/catch/finally
来捕获代码块反常,这个和其它编程语言类似。
- Dart中能够经过
Flutter框架反常捕获
- Flutter 框架在很多关键的办法进行了反常捕获。
- 举一个比如,当布局产生越界或不合规范时,Flutter就会自动弹出一个过错界面,这是因为Flutter已经在履行build办法时添加了反常捕获。
- 终究的源码如下,具体看 ComponentElement 类中的 performRebuild() 办法。
@override void performRebuild() { try { //履行build办法 built = build(); } catch (e, stack) { // 有反常时则弹出过错提示 built = ErrorWidget.builder(_debugReportException('building $this', e, stack)); } }
- 能够看到,在产生反常时,Flutter默许的处理方法是弹一个ErrorWidget
- 但假如想自己捕获反常并上报到报警渠道的话应该怎么做?进入
_debugReportException()
办法看看:
FlutterErrorDetails _debugReportException( String context, dynamic exception, StackTrace stack, { InformationCollector informationCollector }) { //构建过错概况目标 final FlutterErrorDetails details = FlutterErrorDetails( exception: exception, stack: stack, library: 'widgets library', context: context, informationCollector: informationCollector, ); //报告过错 FlutterError.reportError(details); return details; }
- 但假如想自己捕获反常并上报到报警渠道的话应该怎么做?进入
- 发现,过错是经过
FlutterError.reportError
办法上报的,继续跟踪:static void reportError(FlutterErrorDetails details) { ... if (onError != null) onError(details); //调用了onError回调 }
- 发现
onError
是FlutterError
的一个静态属性,它有一个默许的处理办法dumpErrorToConsole
,到这儿就明晰了,假如想自己上报反常,只需要提供一个自界说的过错处理回调即可,如:void main() { FlutterError.onError = (FlutterErrorDetails details) { reportError(details); }; ... }
- 这样就能够处理那些Flutter为我们捕获的反常了,接下来看看怎么捕获其它反常。
其它反常捕获与日志收集
-
在Flutter中,还有一些Flutter没有为我们捕获的反常,如调用空目标办法反常、Future中的反常。
- 在Dart中,反常分两类:同步反常和异步反常,同步反常能够经过
try/catch
捕获,而异步反常则比较费事,如下面的代码是捕获不了Future
的反常的:
try{ Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx")); }catch (e){ print(e) }
- 在Dart中,反常分两类:同步反常和异步反常,同步反常能够经过
-
Dart中有一个
runZoned(...)
办法,能够给履行目标指定一个Zone。- Zone表示一个代码履行的环境规模,为了便利了解,能够将Zone类比为一个代码履行沙箱,不同沙箱的之间是隔离的,沙箱能够捕获、阻拦或修正一些代码行为。
- 如Zone中能够捕获日志输出、Timer创立、微使命调度的行为,一起Zone也能够捕获所有未处理的反常。
- 下面看看
runZoned(...)
办法界说:
R runZoned<R>(R body(), { Map zoneValues, ZoneSpecification zoneSpecification, Function onError, })
- zoneValues: Zone 的私有数据,能够经过实例
zone[key]
获取,能够了解为每个“沙箱”的私有数据。 - zoneSpecification:Zone的一些装备,能够自界说一些代码行为,比方阻拦日志输出行为等,举个比如:
-
下面是阻拦应用中所有调用
print
输出日志的行为。main() { runZoned(() => runApp(MyApp()), zoneSpecification: new ZoneSpecification( print: (Zone self, ZoneDelegate parent, Zone zone, String line) { parent.print(zone, "Intercepted: $line"); }), ); }
- 这样一来,APP中所有调用
print
办法输出日志的行为都会被阻拦,经过这种方法,也能够在应用中记录日志,等到应用触发未捕获的反常时,将反常信息和日志统一上报。ZoneSpecification还能够自界说一些其他行为,读者能够检查API文档。
- 这样一来,APP中所有调用
-
onError:Zone中未捕获反常处理回调,假如开发者提供了onError回调或者经过
ZoneSpecification.handleUncaughtError
指定了过错处理回调,那么这个zone将会变成一个error-zone,该error-zone中产生未捕获反常(不管同步还是异步)时都会调用开发者提供的回调,如:runZoned(() { runApp(MyApp()); }, onError: (Object obj, StackTrace stack) { var details=makeDetails(obj,stack); reportError(details); });
结合上面的
FlutterError.onError
就能够捕获我们Flutter应用中全部过错了!需要注意的是,error-zone内部产生的过错是不会跨过当时error-zone的鸿沟的,假如想跨过error-zone鸿沟去捕获反常,能够经过共同的“源”zone来捕获,如:var future = new Future.value(499); runZoned(() { var future2 = future.then((_) { throw "error in first error-zone"; }); runZoned(() { var future3 = future2.catchError((e) { print("Never reached!"); }); }, onError: (e) { print("unused error handler"); }); }, onError: (e) { print("catches error of first error-zone."); });
总结
- 终究反常捕获和上报代码如下:
void collectLog(String line){ ... //收集日志 } void reportErrorAndLog(FlutterErrorDetails details){ ... //上报过错和日志逻辑 } FlutterErrorDetails makeDetails(Object obj, StackTrace stack){ ...// 构建过错信息 } void main() { FlutterError.onError = (FlutterErrorDetails details) { reportErrorAndLog(details); }; runZoned( () => runApp(MyApp()), zoneSpecification: ZoneSpecification( print: (Zone self, ZoneDelegate parent, Zone zone, String line) { collectLog(line); // 收集日志 }, ), onError: (Object obj, StackTrace stack) { var details = makeDetails(obj, stack); reportErrorAndLog(details); }, ); }