Flutter 从源码扒一扒Stream机制

embedded/2024/10/22 15:27:23/

Stream的基本使用

//1、创建一个流控制对象,只要用来控制流的暂停、取消和订阅
StreamController _controller = StreamController();//2、实现对一个流的订阅和监听事件
_controller.stream.listen((event) {
print("event==$event");
});//3、添加一个事件
_controller.add("123");

StreamController类

职责是一个抽象类,用于创建一个可以发送数据和接收数据的可监听对象。
_StreamController 是StreamController的真正实现类
工厂构造方法

factory StreamController({void onListen()?,void onPause()?,void onResume()?,FutureOr<void> onCancel()?,bool sync = false}) {return sync? _SyncStreamController<T>(onListen, onPause, onResume, onCancel): _AsyncStreamController<T>(onListen, onPause, onResume, onCancel);

我们使用StreamController的时候会这样
StreamController controller = StreamController();
但是StreamController是个抽象,不能被实例化,主要他使用了工厂构造,最后使用他的子类去实例化。
还要一个字段 bool sync = false ,false表示的是异步的,true表示同步。不更改的话,默认是异步的

_SyncStreamController和_AsyncStreamController就是StreamController的子类

//异步控制器
class _AsyncStreamController<T> = _StreamController<T>with _AsyncStreamControllerDispatch<T>;//同步控制器
class _SyncStreamController<T> = _StreamController<T>with _SyncStreamControllerDispatch<T>;

可以看到_AsyncStreamController和_SyncStreamController直接继承了_StreamController。

而_StreamController是StreamController的真正的实现类

//继承关系链
abstract class _StreamController<T> implements _StreamControllerBase<T> abstract class _StreamControllerBase<T>implementsStreamController<T>,_StreamControllerLifecycle<T>,_EventSink<T>,_EventDispatch<T> {

_controller.stream就是Stream对象

Stream 提供了一种接收事件序列的方法。每个事件要么是数据事件(也称为流的元素),要么是错误事件(这是某件事情失败的通知)。

这个流机制也算是一个生产者和消费者模式。
生产者就是_controller.add(“123”); 消费者就是listen监听了
那我们来看看stream内部是如何实现这个机制的?

listen监听

我们得先注册一下监听事件,添加对此流的订阅,才能接收到生产者的通知。

  StreamSubscription<T> listen(void onData(T event)?,{Function? onError, void onDone()?, bool? cancelOnError});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;}这个listen方法是Stream类的方法,返回的是一个订阅对象StreamSubscription。并且是空的实现,需要子类去实现它。我们看看是哪个类实现了Stream类。它有很多的实现类,我选一个主要的类_StreamImpl。_StreamImpl 是 Stream的继承类,它也是个抽象类,我去找它的最终实现类_ControllerStreamabstract class _StreamImpl<T> extends Stream<T>class _ControllerStream<T> extends _StreamImpl<T>我们可以发现在_StreamController 有个方法,用来获取流对象的,正好是Stream的子类_ControllerStreamStream<T> get stream => _ControllerStream<T>(this);我们来看看_ControllerStream类StreamSubscription<T> _createSubscription(void onData(T data)?,Function? onError, void onDone()?, bool cancelOnError) =>_controller._subscribe(onData, onError, onDone, cancelOnError);

_controller就是抽象类_StreamControllerLifecycle的实例对象,
_subscribe是个抽象方法,需要子类去实现它。

abstract class _StreamController<T> implements _StreamControllerBase<T>
abstract class _StreamControllerBase<T>implementsStreamController<T>,_StreamControllerLifecycle<T>,EventSink<T>,_EventDispatch<T>

从上面的代码可以看出,最终是_StreamController实现了_subscribe方法
我们把_subscribe方法代码拿出来

StreamSubscription<T> _subscribe(void onData(T data)?, Function? onError,void onDone()?, bool cancelOnError) {if (!_isInitialState) {throw StateError("Stream has already been listened to.");}//创建一个订阅对象_ControllerSubscription<T> subscription = _ControllerSubscription<T>(this, onData, onError, onDone, cancelOnError);_PendingEvents<T>? pendingEvents = _pendingEvents;_state |= _STATE_SUBSCRIBED;if (_isAddingStream) {_StreamControllerAddStreamState<T> addState = _varData as dynamic;addState.varData = subscription;addState.resume();} else {_varData = subscription;}subscription._setPendingEvents(pendingEvents);subscription._guardCallback(() {_runGuarded(onListen);});return subscription;}

这个方法是关键,从方法名字我们就可以知道这是一个订阅方法,实现对一个事件的订阅。
我们看一下_ControllerSubscription,它的父类是StreamSubscription。

StreamSubscription 类

StreamSubscription 是一个抽象接口类
文档上描述 订阅向listen提供事件,并保存用于处理事件的回调。订阅还可用于取消订阅事件,或暂时暂停流中的事件。

下面是它的一些抽象方法

    ///取消订阅事件Future<void> cancel();///处理订阅事件void onData(void handleData(T data)?);///完成订阅事件void onDone(void handleDone()?);///暂停订阅事件void pause([Future<void>? resumeSignal]);///恢复订阅事件void resume();
class _BufferingStreamSubscription<T>implements StreamSubscription<T>, _EventSink<T>, _EventDispatch<T>class _ControllerSubscription<T> extends _BufferingStreamSubscription<T> 

发起数据通知

接着我们再看看数据添加方法,这个方法就是用来发起数据通知的

   if (hasListener) {_sendData(value);} else if (_isInitialState) {_ensurePendingEvents().add(_DelayedData<T>(value));}}// 这个_sendData方法是抽象类_EventDispatch的方法abstract class _EventDispatch<T> {void _sendData(T data);void _sendError(Object error, StackTrace stackTrace);void _sendDone();}

经过查找发现 _AsyncStreamControllerDispatch类实现了_sendData的方法

继承关系链

abstract class _StreamController<T> implements _StreamControllerBase<T>
abstract class _StreamControllerBase<T> implements StreamController<T>, _StreamControllerLifecycle<T>,_EventSink<T>,_EventDispatch<T> {}_AsyncStreamControllerDispatch类实现的方法如下void _sendData(T data) {_subscription._addPending(_DelayedData<T>(data));}_subscription 就是_ControllerSubscription的实例对象
如下代码所示_ControllerSubscription<T> get _subscription {assert(hasListener);Object? varData = _varData;if (_isAddingStream) {_StreamControllerAddStreamState<Object?> streamState = varData as dynamic;varData = streamState.varData;}return varData as dynamic;}varData 这个很熟悉就是上面的_subscribe方法里面实例化ControllerSubscription对象赋值给varData,这个varData实际就是ControllerSubscription对象。

继承关系链

class _ControllerSubscription<T> extends _BufferingStreamSubscription<T>
class _BufferingStreamSubscription<T> implements StreamSubscription<T>, _EventSink<T>, _EventDispatch<T>

接着我们再来看看 _subscription._addPending(_DelayedData(data));

代码如下,这个方法是_BufferingStreamSubscription类实现的

   void _addPending(_DelayedEvent event) {var pending = _pending ??= _PendingEvents<T>();pending.add(event);if (!_hasPending) {_state |= _STATE_HAS_PENDING;if (!_isPaused) {pending.schedule(this);}}}

这个方法主要是添加一个处理事件,接着我们看看schedule方法

   void schedule(_EventDispatch<T> dispatch) {if (isScheduled) return;assert(!isEmpty);if (_eventScheduled) {assert(_state == stateCanceled);_state = stateScheduled;return;}scheduleMicrotask(() {int oldState = _state;_state = stateUnscheduled;if (oldState == stateCanceled) return;handleNext(dispatch);});_state = stateScheduled;}放到微任务中执行handleNextvoid handleNext(_EventDispatch<T> dispatch) {assert(!isScheduled);assert(!isEmpty);_DelayedEvent event = firstPendingEvent!;_DelayedEvent? nextEvent = event.next;firstPendingEvent = nextEvent;if (nextEvent == null) {lastPendingEvent = null;}event.perform(dispatch);}

_DelayedEvent是个抽象类,perform方法是需要子类去实现
它的子类就是_DelayedData

class _DelayedData<T> extends _DelayedEvent<T> {final T value;_DelayedData(this.value);void perform(_EventDispatch<T> dispatch) {dispatch._sendData(value);}
}

我们看_sendData这个方法,这个方法也是在_BufferingStreamSubscription类中实现的。
整个代码看下来我们只需要关心 _zone.runUnaryGuarded(_onData, data); 这个是实现数据发送的关键。
也是订阅者和被订阅者之间的中间角色,起着承上启下的作用。

 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 被称做是沙箱,在 Dart 中,Zone 是用于隔离代码执行环境的概念。Zone 可以看作是一种执行上下文,它可以用于控制代码执行过程中的一些行为,比如异常处理、日志记录、资源管理等。Zone 中的 run 方法允许你在指定的 Zone 中运行一段代码块。
这里不继续深究下去,有兴趣的可以看看。我们只需要知道调用runUnaryGuarded这个方法。它就会回调_onData函数,并将参数data传给_onData方法。
_onData这个函数就是我们listen监听里的匿名函数,最终会回调那里去。

最后结语

虽然看不懂源码是一件枯燥无聊的事,但是多看几遍或许会变成一件有趣的事。如果要想深入学习flutter,阅读源码是必须要跨过的坎。
当然我写的也不是很好,只是纯粹记录学习从步骤一步步看源码的过程。有不满的,可以提提意见,但不要乱喷。


http://www.ppmy.cn/embedded/10661.html

相关文章

制造企业研发设计资源用共享云桌面集中管控有哪些优势?

在制造企业上云的过程中&#xff0c;因为它们多用3D设计软件&#xff0c;所以选择一款高效、稳定、安全的云桌面产品显得尤为重要。云飞云共享云桌面作为一种新型的云桌面产品&#xff0c;正逐渐受到越来越多制造企业的青睐。那么&#xff0c;制造企业为什么要选云飞云共享云桌…

书生·浦语大模型-第四节课笔记/作业

笔记 作业 xtuner train ./config/internlm2_chat_7b_qlora_alpaca_e3_copy.py --work-dir ./train微调前效果 微调后效果 微调过程-transformer-lora

Linux 自动分区的调整策略

不少人在安装Linux系统时&#xff0c;分区这一步选择默认&#xff0c;会出现根分区比较小&#xff0c;home分区会比较大的情况。一段时间运行后&#xff0c;由于/var目录位于根分区&#xff0c;日常运行产生的日志等文件会占满根分区空间&#xff0c;会出现程序报错或者空间报警…

学习Rust的第11天:模块系统

Rust的模块系统可以使用它来管理不断增长的项目&#xff0c;并跟踪 modules 存储在何处。 Rust的模块系统是将代码组织成逻辑片段的有效工具&#xff0c;因此可以实现代码维护和重用。模块支持分层组织、隐私管理和代码封装。Rust为开发人员提供了多功能和可扩展的方法来管理项…

vue3+vite动态路由的实现

之前写过一个一篇关于vue3 cli的文章&#xff0c;状态管理用的vuex&#xff0c;需要参考的可以看下vue3动态路由的实现 现在把项目换成vite&#xff0c;有些地方不同&#xff0c;关于路由的处理也有所改变&#xff0c;所以重新记录一下 需要用到js-cookie、pinia 一、登录请求获…

当服务器代码出问题时,相信你会用到这些命令

压测 jconsle远程 java -Djava.rmi.server.hostname{{本机地址}} -Dcom.sun.management.jmxremotetrue -Dcom.sun.management.jmxremote.port3214 -Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.rmi.port3214 -Dcom.sun.management.jmxremot…

算法小白刷力扣 1 - 两数之和

题目描述 原题链接&#xff1a;https://leetcode.cn/problems/two-sum/description/ 给定一个整数数组nums和一个整数目标值target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。…

李沐53_语言模型——自学笔记

语言模型 1.预测文本序列出现的概率 2.应用在做预训练模型 3.生成文本&#xff0c;给定前面几个词&#xff0c;不断生成后续文本 4.判断多个序列中哪个更常见 真实数据集的统计 《时光机器》数据集构建词表&#xff0c; 并打印前10个最常用的&#xff08;频率最高的&…