今天开始看gsy_github_app_flutter
入口文件就来了个runZoned
,代码如下
void main() {
runZoned(() {
ErrorWidget.builder = (FlutterErrorDetails details) {
Zone.current.handleUncaughtError(details.exception, details.stack);
return ErrorPage(
details.exception.toString() + "\n " + details.stack.toString(), details);
};
runApp(ConfigWrapper(
child: FlutterReduxApp(),
config: EnvConfig.fromJson(config),
));
}, onError: (Object obj, StackTrace stack) {
print(obj);
print(stack);
});
}
ErrorWidget.builder
Flutter就会自动弹出一个错误界面,这是因为Flutter已经在执行build方法时添加了异常捕获,最终的源码如下:
@override
void performRebuild() {
...
try {
//执行build方法
built = build();
} catch (e, stack) {
// 有异常时则弹出错误提示
built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
}
...
}
可以看到,在发生异常时,Flutter默认的处理方式时弹一个ErrorWidget
Flutter就会自动弹出一个错误界面,这是因为Flutter已经在执行build方法时添加了异常捕获,最终的源码如下:
@override
void performRebuild() {
...
try {
//执行build方法
built = build();
} catch (e, stack) {
// 有异常时则弹出错误提示
built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
}
...
}
可以看到,在发生异常时,Flutter默认的处理方式时弹一个ErrorWidget,所以ErrorWidget.builder
就是用于生成widget时错误时替换默认错误显示的,就是替换那个全红的报错界面。
runZoned
在Flutter中,还有一些Flutter没有为我们捕获的异常,如调用空对象方法异常、Future
中的异常。在Dart中,异常分两类:同步异常和异步异常,同步异常可以通过try/catch
捕获,而异步异常则比较麻烦,如下面的代码是捕获不了Future的
异常的:
try{
Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
}catch (e){
print(e)
}
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文档。
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."); }
);
本文资料来源
网友评论