Flutter中的事件匯流排(EventBus)

mwq30123發表於2020-05-07

EventBus介紹

在Android事件匯流排庫中,EventBus庫是最廣泛之一。 看看Android EventBus庫的說明 官網

  • 釋出者/訂閱者模式
  • 集中式通訊
  • 低耦合,簡化元件通訊

EventBus-Publish-Subscribe.png

自定義EventBus

我們要實現一個Flutter版的EventBus!功能如下:

  • 註冊Subscriber到EventBus
  • Publisher post Event,分發給Subscriber
  • EventBus可以註冊多種型別的Event

Demo地址 入口 main_eventbus.dart

介面定義

//釋出者介面
abstract class IPublisher {
  void post<T>(T event);
}
//訂閱者:函式物件
typedef ISubscriber<T> = void Function(T event);

//集中式通訊,
//1.IEventBus繼承IPublisher,分發資料
//2.IEventBus註冊和取消註冊ISubscriber
abstract class IEventBus extends IPublisher {
  
  void register<T>(ISubscriber<T> subscriber);

  void unregister<T>(ISubscriber<T> subscriber);
}
複製程式碼

介面實現1

Type typeOf<T>() => T;

class MyEventBus1 implements IEventBus {
  //我們用map存放我們的訂閱者。不同訂閱者訂閱的Event型別可能是不同的
  Map<Type, List<Function>> map = new Map();

  @override
  void register<T>(ISubscriber<T> subscriber) {
    Type type = typeOf<T>();
    if (!map.containsKey(type)) {
      map[type] = new List();
    }
    map[type].add(subscriber);
  }

  @override
  void unregister<T>(ISubscriber<T> subscriber) {
    Type type = typeOf<T>();
    if (map.containsKey(type)) {
      map[type].remove(subscriber);
    }
  }

  //釋出
  @override
  void post<T>(T event) {
    Type type = typeOf<T>();
    if (map.containsKey(type)) {
      var subscribers = map[type];
      subscribers?.forEach((subscriber) => subscriber?.call(event));
    }
  }
}
複製程式碼

介面實現2

class MyEventBus2 implements IEventBus {
  List<Function> subscribers = new List();

  @override
  register<T>(ISubscriber<T> subscriber) {
    if (!subscribers.contains(subscriber)) {
      subscribers.add(subscriber);
    }
  }

  @override
  unregister<T>(ISubscriber<T> subscriber) {
    if (subscribers.contains(subscriber)) {
      subscribers.remove(subscriber);
    }
  }

  @override
  post<T>(T event) {
    var ints = subscribers.whereType<ISubscriber<T>>();
    ints?.forEach((subscriber) => subscriber?.call(event));
  }
}
複製程式碼

功能測試

class EventX {}

class EventY {}

main() {
  testEventBus(new MyEventBus1());
  print("--------------------");
  testEventBus(new MyEventBus2());
}

void testEventBus(IEventBus eventBus) {
  ISubscriber<EventX> subscriber1 = (event) => print(event.toString());
  ISubscriber<EventX> subscriber2 = (event) => print(event.toString());
  eventBus.register(subscriber1);
  eventBus.register(subscriber2);
  eventBus.unregister(subscriber1);

  ISubscriber<EventY> subscriber3 = (event) => print(event.toString());
  ISubscriber<EventY> subscriber4 = (event) => print(event.toString());
  eventBus.register(subscriber3);
  eventBus.register(subscriber4);

  eventBus.post(new EventX());
  eventBus.post(new EventY());
}
複製程式碼

結果輸出為以下,測試通過

Instance of 'EventX'
Instance of 'EventY'
Instance of 'EventY'
--------------------
Instance of 'EventX'
Instance of 'EventY'
Instance of 'EventY'
複製程式碼

使用自定義EventBus

需求效果圖

點選每行的按鈕,每行上的兩個數字會增長1

c.gif

程式碼實現

MyEventBus1 _eventBus = new MyEventBus1();
//首先我們定義event。第一排用EventA,第二排用EventB
class BaseEvent { int count = 0;}
class EventA extends BaseEvent {}
class EventB extends BaseEvent {}

//點選按鈕
class ButtonWidget<T extends BaseEvent> extends StatelessWidget {
  final T event;

  const ButtonWidget({Key key, this.event}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("increment"),
      onPressed: _increment,
    );
  }

  //點選處理
  void _increment() {
    if (event != null) {
      event.count++;
      _eventBus.post(event);
    }
  }
}

//顯示數字
class TextWidget<T> extends StatefulWidget {
  @override
  _TextWidgetState<T> createState() {
    return _TextWidgetState<T>();
  }
}

class _TextWidgetState<T> extends State<TextWidget<T>> {
  int _count = 0;
  ISubscriber<T> _subscriber;

  @override
  void initState() {
    super.initState();
    //通過setState 重新整理UI。
    
    _subscriber =
        (event) => setState(() => _count = (event as BaseEvent).count);
   //註冊
    _eventBus.register<T>(_subscriber);
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      "   $_count  ",
      style: TextStyle(fontSize: 18),
    );
  }

  @override
  void dispose() {
    super.dispose();
    //取消註冊
    _eventBus.unregister<T>(_subscriber);
  }
}

//頁面主體
class EventBusDemoWidget1 extends StatefulWidget {
  EventBusDemoWidget1({Key key}) : super(key: key);

  @override
  _EventBusDemoWidget1State createState() => _EventBusDemoWidget1State();
}

class _EventBusDemoWidget1State extends State<EventBusDemoWidget1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(eventBusDemo1Title),
      ),
      body: Container(
        child: Column(
          children: <Widget>[
            Row(
              children: <Widget>[
                new ButtonWidget(event: new EventA()),
                new TextWidget<EventA>(),
                new TextWidget<EventA>()
              ],
            ),
            Divider(
              color: Colors.grey,
            ),
            Row(
              children: <Widget>[
                new ButtonWidget(event: new EventB()),
                new TextWidget<EventB>(),
                new TextWidget<EventB>()
              ],
            )
          ],
        ),
      ),
    );
  }
}
複製程式碼

自定義EventBus大功告成!再來看看別人家的EventBus。

pub上的EventBus

我們在pub.dev上找到了一個EventBus庫 ,github地址

An Event Bus using Dart Streams for decoupling applications

使用EventBus

實際相對於自定義的EventBus,不同點在ButtonWidget和TextWidget中。

class ButtonWidget<T extends BaseEvent> extends StatelessWidget {
  final T event;

  const ButtonWidget({Key key, this.event}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("increment"),
      onPressed: _increment,
    );
  }

  void _increment() {
    if (event != null) {
      event.count++;
      print(event.count);
      _eventBus.fire(event);
//      _eventBus.post(event);
    }
  }
}

class TextWidget<T> extends StatefulWidget {
  @override
  _TextWidgetState<T> createState() {
    return _TextWidgetState<T>();
  }
}

class _TextWidgetState<T> extends State<TextWidget<T>> {
  int _count = 0;
//  ISubscriber<T> _subscriber;
  StreamSubscription<T> _subscriber;

  @override
  void initState() {
    super.initState();
//    _subscriber =
//        (event) => setState(() => _count = (event as BaseEvent).count);
//    _eventBus.register<T>(_subscriber);
    _subscriber = _eventBus
        .on<T>()
        .listen((event) => setState(() => _count = (event as BaseEvent).count));
  }

  @override
  Widget build(BuildContext context) {
    print(typeOf<T>());
    return Text(
      "   $_count  ",
      style: TextStyle(fontSize: 18),
    );
  }

  @override
  void dispose() {
    super.dispose();
    //    _eventBus.unregister<T>(_subscriber);
    _subscriber.cancel();
  }
}
  
複製程式碼

EventBus庫原始碼

class EventBus {
  StreamController _streamController;

  StreamController get streamController => _streamController;

  EventBus({bool sync = false})
      : _streamController = StreamController.broadcast(sync: sync);

  EventBus.customController(StreamController controller)
      : _streamController = controller;

  Stream<T> on<T>() {
    if (T == dynamic) {
      return streamController.stream;
    } else {
      return streamController.stream.where((event) => event is T).cast<T>();
    }
  }

  void fire(event) {
    streamController.add(event);
  }

  void destroy() {
    _streamController.close();
  }
}
複製程式碼

EventBus庫之Stream

不禁感慨,Dart自帶Stream,處理非同步事件。StreamDart:async庫的核心API,對非同步提供了非常好的支援。 這裡簡單貼圖,體會下Stream。

截圖2020-05-07上午11.43.57.png
值得注意的是這句話

return streamController.stream.where((event) => event is T).cast<T>();
複製程式碼

返回的Stream包了兩層 CastStream(_WhereStream(_BroadcastStream),通過這樣的處理就可以監聽泛型事件了。

_WhereStream先處理


  class _WhereStream<T> extends _ForwardingStream<T, T> {
  final _Predicate<T> _test;

  _WhereStream(Stream<T> source, bool test(T value))
      : _test = test,
        super(source);

    //_test即為 (event) => event is T
  void _handleData(T inputEvent, _EventSink<T> sink) {
    bool  satisfies = _test(inputEvent);
    if (satisfies) {
      sink._add(inputEvent);
    }
  }
}
複製程式碼

CastStreamSubscription實際做的型別轉換

class CastStreamSubscription<S, T> implements StreamSubscription<T> {
  final StreamSubscription<S> _source;

  /// User's data handler. May be null.
  void Function(T) _handleData;

  CastStreamSubscription(this._source) {
    _source.onData(_onData);
  }
  void _onData(S data) {
    if (_handleData == null) return;
    T targetData = data as T;
    _zone.runUnaryGuarded(_handleData, targetData);
  }
複製程式碼

後記

至此,我們在Provider庫StreamProvider中遇到了Stream,我們在EventBus庫中遇到了Stream Dart語言自帶優秀的非同步程式設計模型 Stream和Future。 Stream擁有良好的非同步模型,滋養了一大波元件庫 Provider/BLoC/flutter_redux/Rxdart/fish-redux。 接下來就有必要對Stream進行分析了。