BroadcastStream
在上一篇我們發(fā)現(xiàn) listen 這個(gè)方法只能被添加一次, 即 _varData 這個(gè)參數(shù)只有一個(gè),是不是只要把他變成PendingData形式的隊(duì)列, 每次回調(diào)的時(shí)候遍歷這個(gè)隊(duì)列就可以通知所有callback 回調(diào)呢,
在普通的StreamController 里判斷了如果 Stream 是 _isInitialState 的,也就是訂閱過(guò)的,就直接報(bào)錯(cuò) "Stream has already been listened to." ,只有未訂閱的才創(chuàng)建 StreamSubscription 。
而在BroadcastStreamController 中去掉了這個(gè)方法變成了close 判斷
StreamSubscription<T> _subscribe(void onData(T data)?, Function? onError,
void onDone()?, bool cancelOnError) {
if (isClosed) {
return new _DoneStreamSubscription<T>(onDone);
}
var subscription = new _BroadcastSubscription<T>(
this, onData, onError, onDone, cancelOnError);
_addListener(subscription);
if (identical(_firstSubscription, _lastSubscription)) {
// Only one listener, so it must be the first listener.
_runGuarded(onListen);
}
return subscription;
}
////_sendData 方法 遍歷了每個(gè)subscription,并添加到任務(wù)隊(duì)列
void _sendData(T data) {
if (_isEmpty) return;
if (_hasOneListener) {
_state |= _BroadcastStreamController._STATE_FIRING;
_BroadcastSubscription<T> firstSubscription =
_firstSubscription as dynamic;
firstSubscription._add(data);
_state &= ~_BroadcastStreamController._STATE_FIRING;
if (_isEmpty) {
_callOnCancel();
}
return;
}
_forEachListener((_BufferingStreamSubscription<T> subscription) {
subscription._add(data);
});
}
說(shuō)到這里自己其實(shí)是有些質(zhì)疑源碼的,為什么我設(shè)置了兩個(gè)監(jiān)聽(tīng)創(chuàng)建了兩個(gè)subscription,你就執(zhí)行了subscription._add(data);兩次,不能只添加一次而依次調(diào)用回調(diào)嗎,帶著這個(gè)疑問(wèn)我后來(lái)我又仔細(xì)想了一下,是不是入?yún)⑹强勺兊?不同的環(huán)境會(huì)造就不同的data,那么是不是可以將方法傳入StreamSink 中呢,但是這也解釋不了為什么添加多次,后來(lái)我仔細(xì)想了一下其實(shí)他們的目的都是為了維護(hù)一個(gè)在Microtask 中的隊(duì)列,如果向我所描述的做法還需要增加一個(gè)callback隊(duì)列,并將BroadCastStreamController 的subscription 由源碼中的隊(duì)列改為一個(gè)單利,兩相抵消暫時(shí)這么做在性能上是沒(méi)有差別的.
在上面我們說(shuō)StreamSink 可以添加耗時(shí)的方法,那么在StreamSink 發(fā)送普通的String int List 數(shù)據(jù)有什么意義嗎,只不過(guò)是在異步中轉(zhuǎn)了一下,其實(shí)普通的數(shù)據(jù)可以作為狀態(tài)通知來(lái)用,但是感覺(jué)這不是他的精髓,他的精髓應(yīng)該是在異步執(zhí)行耗時(shí)任務(wù),onlisten得到結(jié)果,但是實(shí)際情況是如果將一個(gè)StreamSink 方法add 后,在listen又得到這個(gè)方法,并沒(méi)有什么意義,后來(lái)又從網(wǎng)上找到了這樣一句話
因?yàn)?microtask 的優(yōu)先級(jí)又高于 event ,所以如果 microtask 太多就可能會(huì)對(duì)觸摸、繪制等外部事件造成阻塞卡頓哦。
也就是說(shuō)如果在microtask 這個(gè)任務(wù)中執(zhí)行耗時(shí)方法可能造成卡頓,到此研究耗時(shí)任務(wù)就放棄了,
Stream 轉(zhuǎn)換
///添加監(jiān)聽(tīng)的方法 ,調(diào)用_createSubscription
StreamSubscription<T> listen(void onData(T value)?,
{Function? onError, void onDone()?, bool? cancelOnError}) {
return _createSubscription(onData, onError, onDone, cancelOnError ?? false);
}
///創(chuàng)建 _ForwardingStreamSubscription
StreamSubscription<T> _createSubscription(void onData(T data)?,
Function? onError, void onDone()?, bool cancelOnError) {
return new _ForwardingStreamSubscription<S, T>(
this, onData, onError, onDone, cancelOnError);
}
/// 轉(zhuǎn)換的過(guò)程中將自身的_handleData 加入回調(diào)隊(duì)列,
_ForwardingStreamSubscription(this._stream, void onData(T data)?,
Function? onError, void onDone()?, bool cancelOnError)
: super(onData, onError, onDone, cancelOnError) {
_subscription = _stream._source
.listen(_handleData, onError: _handleError, onDone: _handleDone);
}
///Stream 轉(zhuǎn)換 通常是繼承自 _ForwardingStream 來(lái)實(shí)現(xiàn)的,
class _WhereStream<T> extends _ForwardingStream<T, T> {
final bool Function(T) _test;
_WhereStream(Stream<T> source, bool test(T value))
: _test = test,
super(source);
///回調(diào)后,又繼續(xù)添加了,這里看出來(lái)的嵌套
void _handleData(T inputEvent, _EventSink<T> sink) {
bool satisfies;
try {
satisfies = _test(inputEvent);
} catch (e, s) {
_addErrorWithReplacement(sink, e, s);
return;
}
if (satisfies) {
sink._add(inputEvent);
}
}
}
他的做法是添加listen ,在listen 回調(diào)的時(shí)候 調(diào)用自身的StreamSink.add(),將自身的_handleData()添加到事件源,在執(zhí)行回調(diào)后重新添加一次,所謂的轉(zhuǎn)換只不過(guò)是嵌套了一層
所以事件變化的本質(zhì)就是,變換都是對(duì) Stream 的 listen 嵌套調(diào)用組成的。
昨天看了一些面試題,上面經(jīng)常會(huì)問(wèn)什么是Stream ,那么你心里的Stream 是什么呢,我來(lái)說(shuō)一下我寫完這兩篇后我對(duì)Stream 的理解,
Stream 是流或者管道,提供了一種同步或異步以隊(duì)列的方式處理微任務(wù)的消息的機(jī)制.
我學(xué)習(xí)flutter的整個(gè)過(guò)程都記錄在里面了
http://www.lxweimin.com/c/36554cb4c804
最后附上demo 地址