什么是Zone
最好的解释就是官方说明
先来看看最核心的定义
A zone represents an environment that remains stable across asynchronous calls.
在异步调用中执行的一个稳定的环境
所以它定下了一个基调,后续我们直接或间接使用它的时候,基本都是出于异步的原因
All code is executed in the context of a zone, available to the code as Zone.current The initial `main` function runs in the context of the default zone ([Zone.root]). Code can be run in a different zone using either [runZoned] or [runZonedGuarded] to create a new zone and run code in it, or [Zone.run] to run code in the context of an existing zone which may have been created earlier using [Zone.fork].
所有的代码都执行在一个叫Zone的上下文中, flutter的程序在启动main方法的时候就默认执行在了一个叫zone.root的 区域 中了 ,同时我们可以通过 runZoned 和 runZoneGuarded的方式创建一个我们自己的zone ,我们可以用Zone.current来查看当前的zone的类型
在程序入口的main()中默认运行在一个root Zone 中 ,你也可以创建自己的Zone, Zone和Zone之间是隔离的, 他们之间无法通信,你能创建出来的Zone 都是从root Zone fork来的
Zone有什么用
对于Zone而言,它有两个构造函数:
- ZoneSpecification
- ZoneValues
ZoneSpecification:其实是Zone内部代码行为的一个提取,我们可以通过它来为Zone设置一些监听。
ZoneValues:Zone的变量,私有变量。
你可以将Zone类比为一个代码执行沙箱,不同沙箱的之间是隔离的,沙箱可以捕获、拦截或修改一些代码行为,如Zone中可以捕获日志输出、Timer创建、微任务调度的行为,同时Zone也可以捕获所有未处理的异常
创建Zone
Dart提供了runZoned 或 runZonedGuarded 方法,支持Zone的快速创建, 并在其中运行代码
// 使用runZonedGuarded函数创建一个新的Zone,并捕获未处理的异常
runZonedGuarded(() {
// 在这里运行你的代码
}, (error, stackTrace) {
// 在这里处理异常
});
// 使用Zone.current.fork方法创建一个新的Zone,并修改print行为
var zone = Zone.current.fork(specification: ZoneSpecification(
print: (self, parent, zone, line) {
// 在这里修改print行为
}
));
// 在新的Zone中运行代码
zone.run(() {
// 在这里运行你的代码
});
Zone是如何工作的
zone 的工作原理是通过 Zone.current.fork() 方法创建一个新的 zone,并且可以通过 ZoneSpecification 参数来自定义一些 zone 的行为 Zone.run() 方法来在新的 zone 中执行代码 zone 中保持一致的环境,并且可以处理一些异步异常, runApp 和任何扩展 runApp 的东西已经在一个区域中运行。对于Flutter App Developers,我们只需要再添加一个特殊的zone 实现,让一个zone 在app 异常时可以将app exception 错误报告给仍在执行的zone。
简而言之,main 是 Zone 0,任何其他声明的 runZoned 或 runGuardedZone 是下一个子区域。
Zone应用的领域
1 捕获异常
在Dart中,异常分两类:同步异常和异步异常,同步异常可以通过try/catch捕获,根据上边Zone的定义, 异步异常我们就可以通过Zone的包装来捕获
//全局异常的捕捉
class AppCatchError {
run(Widget app) async {
///Flutter 框架异常
FlutterError.onError = (FlutterErrorDetails details) async {
///线上环境
///TODO
if (kReleaseMode) {
Zone.current.handleUncaughtError(details.exception, details.stack!);
} else {
//开发期间 print
FlutterError.dumpErrorToConsole(details);
}
};
runZonedGuarded(() {
//受保护的代码块
Future.delayed(const Duration(seconds: 1), () {
runApp(app);
});
}, (error, stack) => catchError(error, stack));
}
///对搜集的 异常进行处理 上报等等
catchError(Object error, StackTrace stack) {
// ToastUtils.show('${error}${stack}');
// print("AppCatchError>>>>>>>>>>: $kReleaseMode"); //是否是 Release版本
_reportServer(error, stack);
// debugPrint('AppCatchError message:$error,stack$stack');
}
_reportServer(Object error, StackTrace stack) {
var errorInfo = '$error,stack$stack';
var userId = LoginUtil.userId();
// 自定义上报
RequestRepository.getInstance().reportError(errorInfo, userId);
}
}
2 使用Zone.bindCallback来绑定一个异步回调函数
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String _text = 'Hello';
void _updateText() async {
// 使用Zone.bindCallback来绑定一个异步回调函数
var callback = Zone.current.bindCallback(() async {
await Future.delayed(Duration(seconds: 1));
return 'World';
});
// 在回调函数执行后更新状态
var result = await callback();
setState(() {
_text = result;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(_text),
ElevatedButton(
onPressed: _updateText,
child: Text('Update'),
),
],
);
}
}
3 拦截修改微任务调度的行为
1 拦截微任务(Microtask)的调度行为
我们都知道微任务是Dart进行事件循环(event loop)的重要组成部分,同样在zone
内也是可以拦截它的调度行为。
main() {
Zone.root;
Zone parentZone = Zone.current.fork(specification:
new ZoneSpecification(registerCallback: <R>(self, parent, zone, f) {
// 调用我们实际注册的回调函数,第一次这里进来的 f 是 start(),第二次进来则是 end()
f();
return f;
}));
parentZone.run(() {
Zone.current.registerCallback(() => start());
print("hello");
Zone.current.registerCallback(() => end());
});
}
void start() {
print("start");
}
void end() {
print("end");
}
打印
start
hello
end
2 修改微任务(Microtask)的调度行为
//创建队列存储Microtask的队列
Queue q = Queue();
runZoned(
() {
scheduleMicrotask(() => print('schedule Microtask1'));
scheduleMicrotask(() => print('schedule Microtask2'));
},
zoneSpecification: ZoneSpecification(
// 拦截print
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, "${DateTime.now()} —— Interceptor: $line");
},
//拦截微任务调度行为并修改
scheduleMicrotask: (Zone self, ZoneDelegate parent, Zone zone, void f()) {
q.add(f);
if (q.length == 2) {
while (q.length > 0) {
final tempF = q.removeLast();
//从队列取出function并执行
parent.scheduleMicrotask(self, tempF);
}
}
}),
);
打印
flutter: 2023-03-03 20:20:23.141450 —— Interceptor: schedule Microtask2
flutter: 2023-03-03 20:20:23.145571 —— Interceptor: schedule Microtask1
4 拦截其他行为
zone还能通过ZoneSpecification
还能拦截该空间的其他一些行为,比如fork、run、registerCallback
5 Stream
这个用就很常用了,可以参考