Zone是什么
zone表明跨异步调用保持稳定的环境。代码总是在zone的上下文中履行,运用
Zone.current
查看当前zone,初始化主函数在Zone.root
中运转的。运用runZoned
创立新的zone,也能够运用zone.run
在先前运用zone.fork
创立现有区域的上下文中运转代码。
上面是官方说法,咱们用能够这样了解:
Zone类为一个代码履行沙箱,不同沙箱的之间是阻隔的,沙箱能够捕获、阻拦或修正一些代码行为,如Zone中能够捕获日志输出、Timer创立、微使命调度的行为,一起Zone也能够捕获一切未处理的异常
程序启动进入main函数会经过runZonedGuarded
fork一个新的zone
@pragma('vm:entry-point')
// ignore: unused_element
void _runMainZoned(Function startMainIsolateFunction,
Function? dartPluginRegistrant,
Function userMainFunction,
List<String> args) {
startMainIsolateFunction(() {
runZonedGuarded<void>(() {
if (dartPluginRegistrant != null) {
dartPluginRegistrant();
}
if (userMainFunction is _ListStringArgFunction) {
(userMainFunction as dynamic)(args);
} else {
userMainFunction();
}
}, (Object error, StackTrace stackTrace) {
_reportUnhandledException(error.toString(), stackTrace.toString());
});
}, null);
}
经过打印也可得知
print(Zone.current.parent == Zone.root); //flutter: true
需要说明的是:
Zone是不能子类化的,经过
Zone.fork
根据父Zone自界说子Zone时,类似于扩展了该父Zone并覆盖了父Zone的办法,实际上并不是新创立一个类。重写的办法会以函数的方式供给,并会把父Zone和当前Zone作为参数传入
Zone运用
捕获异步履行的过错和阻拦打印
在Dart中,异常分为两种:同步和异步,咱们能够运用try/catch
来捕获同步下的过错,但是在异步情况下无法运用try/catch
捕获,
try{
Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
}catch (e){
print(e)
}
此刻咱们能够运用runZoned()
给履行代码指定一个zone。在此区域内的异步代码都会被该zone捕获到。
runZoned(...)
办法界说:
R runZoned<R>(R body(), {
Map zoneValues,
ZoneSpecification zoneSpecification,
})
经过Zone.fork
创立一个根据[zoneSpecification]
和[zoneValues]
的新zone。
然后运转body
内的代码,并回来成果
zoneValues
:Zone 的私有数据,能够经过实例zone[key]
获取,能够了解为每个“沙箱”的私有数据。
zoneSpecification
:Zone的一些配置,能够自界说一些代码行为,比如阻拦日志输出和过错等,阻拦日志输出并捕获该区域内的过错:
runZoned(
() {
Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
Future.delayed(Duration(seconds: 2)).then((e) => print('ABCD'));
},
zoneSpecification: ZoneSpecification(
// 阻拦print
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, "Interceptor: $line");
},
// 阻拦未处理的异步过错
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
Object error, StackTrace stackTrace) {
parent.print(zone, '${error.toString()} $stackTrace');
},
),
);
阻拦修正Timer创立行为
阻拦timer的创立行为
runZoned(
() {
Future.delayed(Duration(seconds: 1))
.then((e) => Future.error("xxxError"));
Future.delayed(Duration(seconds: 2))
.then((e) => print('FutureDelayedFunction'));
Timer(Duration(seconds: 3), () => print('timer1F'));
Timer(Duration(seconds: 4), () => print('timer2F'));
},
zoneSpecification: ZoneSpecification(
// 阻拦print
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, "${DateTime.now()} —— Interceptor: $line");
},
// 阻拦未处理的异步过错
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
Object error, StackTrace stackTrace) {
parent.print(
zone, '${DateTime.now()} ——' + '${error.toString()} $stackTrace');
},
//阻拦timer
createTimer: (Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void f()) {
return parent.createTimer(self, duration, f);
},
),
);
打印成果
flutter: 2022-03-03 20:01:51.497784 ——xxxError
flutter: 2022-03-03 20:01:52.495162 —— Interceptor: FutureDelayedFunction
flutter: 2022-03-03 20:01:53.491801 —— Interceptor: timer1F
flutter: 2022-03-03 20:01:54.492453 —— Interceptor: timer2F
修正Timer的创立行为
如果只是这样咱们并不能看出来Timer
是否被真实阻拦,咱们修正一下代码
//阻拦timer创立行为 并修正
createTimer: (Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void f()) {
Timer newTimer = parent.createTimer(
self, Duration(seconds: 3), () => print('你们被替换了'));
return newTimer;
},
打印成果
flutter: 2022-03-03 20:05:41.343419 —— Interceptor: 你们被替换了
flutter: 2022-03-03 20:05:41.348701 —— Interceptor: 你们被替换了
flutter: 2022-03-03 20:05:41.348984 —— Interceptor: 你们被替换了
flutter: 2022-03-03 20:05:41.349245 —— Interceptor: 你们被替换了
留意
由于Future.delayed
内部也是由Timer实现,所以也会被阻拦修正。
阻拦修正微使命调度的行为
阻拦微使命(Microtask)的调度行为
咱们都知道微使命是Dart进行事情循环(event loop)的重要组成部分,同样在zone
内也是能够阻拦阻拦它的调度行为。
runZoned(
() {
scheduleMicrotask(() => print('schedule Microtask'));
},
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()) {
parent.scheduleMicrotask(self, f);
}
),
);
修正微使命(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: 2022-03-03 20:20:23.141450 —— Interceptor: schedule Microtask2
flutter: 2022-03-03 20:20:23.145571 —— Interceptor: schedule Microtask1
其他的一些行为阻拦
zone还能经过ZoneSpecification
还能阻拦该空间的其他一些行为,比如fork、run、registerCallback
const factory ZoneSpecification(
{HandleUncaughtErrorHandler? handleUncaughtError,
RunHandler? run,
RunUnaryHandler? runUnary,
RunBinaryHandler? runBinary,
RegisterCallbackHandler? registerCallback,
RegisterUnaryCallbackHandler? registerUnaryCallback,
RegisterBinaryCallbackHandler? registerBinaryCallback,
ErrorCallbackHandler? errorCallback,
ScheduleMicrotaskHandler? scheduleMicrotask,
CreateTimerHandler? createTimer,
CreatePeriodicTimerHandler? createPeriodicTimer,
PrintHandler? print,
ForkHandler? fork}) = _ZoneSpecification;
经过Zone来窥探Stream的实现原理
关于Stream的详细介绍及原理能够看这儿
Stream目标
- StreamController:用于整个Stream进程的操控,供给各类接口用于创立各种事情流。
- StreamSink:事情的进口,
add
,addStream
等。 - Stream:事情源自身,一般可用于监听事情或许对事情进行转化,如
listen
、where
。 - StreamSubscription:事情订阅后的目标,表面上用于管理订阅过各类操作,如
cancel
、
Stream作业原理
-
Stream
在listen
的时候传入onData
回调,这个回调会传入到StreamSubscription
中,之后经过zone.registerUnaryCallback
注册得到_onData
函数目标 -
StreamSink
在增加事情时,会履行到StreamSubscription
中的_sendData
办法,然后经过_zone.runUnaryGuarded(_onData, data)
履行1中得到的_onData
函数目标,触发listen
时传入的回调办法
咱们在源码出也能看到的确如此
//1.listen传入onData回调到StreamSubscription中
StreamSubscription<T> listen(void onData(T data)?,
{Function? onError, void onDone()?, bool? cancelOnError}) {
cancelOnError ??= false;
StreamSubscription<T> subscription =
_createSubscription(onData, onError, onDone, cancelOnError);
_onListen(subscription);
return subscription;
}
//为节约篇幅,已省略部分代码
//在此,已经获取到_onData函数目标
_onData = _registerDataHandler<T>(_zone, onData),
//把onData传入进行注册
static void Function(T) _registerDataHandler<T>(
Zone zone, void Function(T)? handleData) {
return zone.registerUnaryCallback<void, T>(handleData ?? _nullDataHandler);
}
//2.sink增加事情StreamSubscription._sendData,然后调用_zone.runUnaryGuarded(_onData, data),
/* _EventDispatch interface. */
void _sendData(T data) {
assert(!_isCanceled);
assert(!_isPaused);
assert(!_inCallback);
bool wasInputPaused = _isInputPaused;
_state |= _STATE_IN_CALLBACK;
_zone.runUnaryGuarded(_onData, data);
_state &= ~_STATE_IN_CALLBACK;
_checkState(wasInputPaused);
}
那么,zone.registerUnaryCallback
和zone.runUnaryGuarded
别离是什么?
//注册一个给定的回调在这个zone里边,得到一个函数目标,类型是ZoneUnaryCallback
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg));
//ZoneUnaryCallback的界说
typedef R ZoneUnaryCallback<R, T>(T arg);
//履行一个给定的action(便是上面的_onData函数目标),参数是argument,并且捕获异步的过错
void runUnaryGuarded<T>(void action(T argument), T argument);
当然这儿还有其他的办法比如registerCallback
,registerBinaryCallback
,差异便是参数数量的问题
至此,咱们已清楚Stream是如何经过zone进行作业的,同样这些行为咱们也是能够进行阻拦并修正的
runZoned(
() {
final streamController = StreamController();
streamController.sink.add('ABC');
streamController.stream.listen((event) {
print(event);
});
},
zoneSpecification: ZoneSpecification(
// 阻拦print
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, "${DateTime.now()} —— Interceptor: $line");
},
registerUnaryCallback: <R, T>(Zone self, ZoneDelegate parent, Zone zone,
R Function(T arg) f) {
return parent.registerUnaryCallback(self, f);
},
runUnary: <R, T>(Zone self, ZoneDelegate parent, Zone zone,
R Function(T arg) f, T arg) {
return parent.runUnary(zone, f, '移花接木' as T);
},
),
);
打印成果
flutter: 2022-03-03 21:45:15.872001 —— Interceptor: 移花接木
参考文章:
/post/684490…
api.flutter-io.cn/flutter/dar…
book.flutterchina.club/chapter2/th…