Zone是什么

zone表明跨异步调用保持稳定的环境。代码总是在zone的上下文中履行,运用Zone.current查看当前zone,初始化主函数在Zone.root中运转的。运用runZoned创立新的zone,也能够运用zone.run在先前运用zone.fork创立现有区域的上下文中运转代码。
上面是官方说法,咱们用能够这样了解:

Zone类为一个代码履行沙箱,不同沙箱的之间是阻隔的,沙箱能够捕获、阻拦或修正一些代码行为,如Zone中能够捕获日志输出、Timer创立、微使命调度的行为,一起Zone也能够捕获一切未处理的异常

程序启动进入main函数会经过runZonedGuardedfork一个新的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:事情源自身,一般可用于监听事情或许对事情进行转化,如listenwhere
  • StreamSubscription:事情订阅后的目标,表面上用于管理订阅过各类操作,如cancel

Stream作业原理

  1. Streamlisten的时候传入onData回调,这个回调会传入到StreamSubscription中,之后经过zone.registerUnaryCallback注册得到_onData函数目标
  2. 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.registerUnaryCallbackzone.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…