什么是EventBus
EventBus是全局事件總線,底層通過Stream
來實現(xiàn);它可以實現(xiàn)不同頁面的跨層訪問,通過Stream
的機制來實現(xiàn)不同widget之間的狀態(tài)共享.
這里我們使用的是官方提供了一個Event_Bus
庫.
在pubsec.yaml文件導(dǎo)入:
event_bus: ^1.1.0
在使用的文件內(nèi):
import 'package:event_bus/event_bus.dart';
使用方式
這里舉個例子:有三個界面A,B,C, 從A進入B, B再進入C. 在C中點擊按鈕,然后A的背景色會變成紅色.這里實際上就形成了跨層的訪問C->A.
首先我們需要定義一個EventBus對象來訂閱事件流.這里我們通過一個工廠方法來創(chuàng)建一個唯一的EventBus對象.
class GlobalEventBus{
EventBus event;
factory GlobalEventBus() => _getInstance();
static GlobalEventBus get instance => _getInstance();
static GlobalEventBus _instance;
GlobalEventBus._internal() {
// 創(chuàng)建對象
event = EventBus();
}
static GlobalEventBus _getInstance() {
if (_instance == null) {
_instance = GlobalEventBus._internal();
}
return _instance;
}
}
用一個全局對象的好處就是不需要將eventbus當(dāng)做參數(shù)進行傳遞了,而且更符合全局總線的概念.
1.首先,我們需要先創(chuàng)建一個事件用來監(jiān)聽,我們將這個事件命名為BackgroundColorChangeEvent
,這個事件其實是一個類,用來當(dāng)做共享數(shù)據(jù)的載體,我們可以在這個類中加入不同的屬性.例如:
class BackgroundColorChangeEvent{
Color color;
BackgroundColorChangeEvent(this.color);
}
這里我們就可以把color
當(dāng)做參數(shù)通過BackgroundColorChangeEvent
對象傳遞到A中,然后改變A的背景色.
2.然后在A訂閱事件:
GlobalEventBus().event.on<BackgroundColorChangeEvent>().listen((event) {
Color color = event.color;
setState(){
_backgroundColor = color;
}
});
以上就完成了事件的創(chuàng)建和訂閱.
3.接著我們就需要在C中發(fā)布這個事件,Event_Bus提供了一個fire
方法來分發(fā)事件.
FlatButton(){
onPress:(){
GlobalEventBus().event.fire(BackgroundColorChangeEvent(Colors.red)) ;
}
}
這里我們通過全局event
對象,分發(fā)了BackgroundColorChangeEvent
事件,并且攜帶了一個color
參數(shù)過去.此時A已經(jīng)訂閱了這個事件,于是背景色就改變了.
需要注意到的是在使用之后需要關(guān)閉event事件流,不然會造成內(nèi)存泄漏,調(diào)用如下代碼即可:
GlobalEventBus().event.destroy();
EventBus的底層實現(xiàn)
以上就是EventBus
的使用方式,非常的簡單.但它底層是怎么實現(xiàn)的呢?我們一步步來分析.
1.創(chuàng)建EventBus
首先我們從EventBus
的初始化開始,進入event_bus.dart
源文件中:
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
我們可以看出,EventBus
對象初始化實際上初始化了一個_streamController
對象,而這個對象是通過StreamController
的broadcast(sync: sync)
方法初始化的,這里大致可以看出來,EventBus
的底層實際上就是通過Stream
來實現(xiàn)的.這里默認帶了sync
參數(shù),表示是否同步,默認為async
.
而進入broadcast(sync: sync)
方法中:
factory StreamController.broadcast(
{void onListen(), void onCancel(), bool sync: false}) {
return sync
? new _SyncBroadcastStreamController<T>(onListen, onCancel) // 同步廣播
: new _AsyncBroadcastStreamController<T>(onListen, onCancel); // 異步廣播
}
可以看到sync
參數(shù)作用就是返回一個是同步或異步廣播流控制器.
這里同步和異步的區(qū)別是:
如果
sync
為true,則事件將通過fire
方法直接傳遞給流的監(jiān)聽者;如果為false,在創(chuàng)建EventBus
之后,才將事件延后傳遞給監(jiān)聽者。
那么事件是怎么被監(jiān)聽到的呢?
2. EventBus進行訂閱監(jiān)聽
事件我們是通過event.on().listen()
方法來監(jiān)聽的,進入on()
的dart源碼中,
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream;
} else {
return streamController.stream.where((event) => event is T).cast<T>();
}
}
我們可以看到on()
返回了一個Stream
,并且用泛型約定了事件類型.如果不傳泛型約定,那么就默認監(jiān)聽所有的事件; 如果添加了泛型T
,那么就只會監(jiān)聽T
這個事件.
然后我們在看listen()
方法:
StreamSubscription<T> listen(void onData(T event),
{Function onError, void onDone(), bool cancelOnError});
在方法參數(shù)中,我們可以發(fā)現(xiàn)onData(T event)
這個方法回調(diào),正是這個方法參數(shù)回調(diào)了我們需要監(jiān)聽訂閱的事件T
.至于怎么接收到這個onData()
回調(diào)的,我們后面再看,先了解下fire()
方法的實現(xiàn).
3. EventBus發(fā)布事件
我們進入fire()
方法中,
void fire(event) {
streamController.add(event);
}
streamController
添加了這個事件,平淡無奇,繼續(xù)看下去.
void add(T value) {
if (!_mayAddEvent) throw _badEventState();
_add(value);
}
_mayAddEvent
表示流關(guān)閉之后或者正在add stream期間,可能無法添加新事件.
在看看_add(value)
:
void _add(T value) {
if (hasListener) {
_sendData(value);
} else if (_isInitialState) {
_ensurePendingEvents().add(new _DelayedData<T>(value));
}
}
如果hasListener
已經(jīng)有被訂閱了,那么就發(fā)送這個事件,如果是_isInitialState
初始化狀態(tài),就掛起這個事件,再add()
一個_DelayedData
;
簡單說下
_DelayedData
,這是一個繼承自_DelayedEvent
的對象,它其實保存了我們這個事件value
,然后利用perform
方法在某個時機進行事件發(fā)放.它的實現(xiàn)如下,
class _DelayedData<T> extends _DelayedEvent<T> {
final T value;
// 保存這個事件
_DelayedData(this.value);
// perform方法用于某個時機發(fā)放這個事件
void perform(_EventDispatch<T> dispatch) {
dispatch._sendData(value);
}
}
再進入_sendData()
方法
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);
}
要看懂這段代碼,我們首先要明白幾個變量和方法.
(1). _zone
: 是一個Zone對象,通過源碼注釋我們可以大概了解一下它的作用:
A zone represents an environment that remains stable across asynchronous calls.
Code is always executed in the context of a zone, available 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],
to create a new zone, or [Zone.run] to run code in the context of
an existing zone likely created using [Zone.fork].
Developers can create a new zone that overrides some of the functionality of an existing zone.
For example, custom zones can replace of modify the behavior of print, timers,
microtasks or how uncaught errors are handled.
zone
表示在異步過程保持穩(wěn)定的一個環(huán)境,類似于iOS沙箱機制.Dart代碼通常都是在這個環(huán)境的上下文中執(zhí)行,在這個環(huán)境中你可以對里面的代碼做很多操作,例如異常捕捉等.而此時這個zone
取值是Zone.current
,就是當(dāng)前的環(huán)境.
(2). runUnaryGuarded(_onData, data)
:這個方法的作用就是在此環(huán)境中用參數(shù)執(zhí)行給定的方法,并捕獲同步錯誤.有點類似于iOS中performSelector:withObject:
方法.指定的方法就是_onData
,參數(shù)就是data
,也就是我們傳遞過來的事件.
(3). _onData
: 這個方法就是我們回調(diào)事件的方法,它是通過上述zone
的_zone.registerUnaryCallback<dynamic, T>(handleData)
方法拿到的.
void onData(void handleData(T event)) {
handleData ??= _nullDataHandler;
// TODO(floitsch): the return type should be 'void', and the type
// should be inferred.
_onData = _zone.registerUnaryCallback<dynamic, T>(handleData);
}
從上面的事件訂閱監(jiān)聽我們知道是在listen()
方法中的onData()
回調(diào)中監(jiān)聽到事件,那很明顯這個_onData
和listen
中的onData
肯定有某種關(guān)聯(lián),知道了這個關(guān)聯(lián),那整個監(jiān)聽流程就通了.
實際上底層有對listen()
中的onData()
進行一層轉(zhuǎn)換,
// onData() ->>> onData(void handleData(T event))
/**
* Replaces the data event handler of this subscription.
*
* The [handleData] function is called for each element of the stream
* after this function is called.
* If [handleData] is `null`, further elements are ignored.
*
* This method replaces the current handler set by the invocation of
* [Stream.listen] or by a previous call to [onData].
*/
void onData(void handleData(T data));
從源碼的注釋可以看出這里的onData()
確實是被替換成了onData(void handleData(T event))
.
那么_zone.registerUnaryCallback<dynamic, T>(handleData)
拿到的_onData
就是就是我們在listen
中傳入的onData()
回調(diào)了.
到這里,訂閱和發(fā)布事件就形成了一個閉環(huán).這也就是EventBus實現(xiàn)的原理. 其實Flutter很多工具底層都是基于Stream
,后面會專門分析一下Stream
的實現(xiàn)原理.