EventBus
介绍
EventBus
是Guava
包里的一个辅助类, 用于实现消息总线模式.
生产事件的叫做Provider
, 消费事件的叫做Subscriber
, 他们之间通过事件类型产生关联.
核心类介绍
EventBus
核心类同步的EventBus, subscriber会和provider在同一个线程上执行. 每个EventBus有一个id标识, 仅做标识展示用, 没有其他作用. 对异常的处理仅仅是log, 因此subscriber自己要做好异常处理, 可以自定义异常处理器.
SubscriberRegistry
Subscriber的注册中心, 登记所有subscriber, 以及维护每种event类型可以被哪些subscriber处理.
-
内部维护了
ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>>,
很容易看出是所有Event类型及其对应的Subscriber. 使用了 ConcurrentHashMap 和 CopyOnWriteArraySet 来保证动态添加订阅关系时的安全性. -
注册一个Subscriber时, 会遍历它的所有父类搜索有注解的接口.
-
当搜索每个event类型可以被哪些subscriber处理时, 他会遍历event类型的所有父类型, 将所有涉及到的Subscriber都加在一起.
@Subscribe
在你的Subscriber类上标记你的方法, 就可以注册为回调.
Dispatcher
负责调用Subscriber, 控制调用顺序.
有下面几种实现, 但通常你无法选择, 因为没有暴露相关接口.
-
PerThreadQueueDispatcher: 可以保证由同一个线程发出的所有事件有序地被subscriber处理, 用一个queuehold住所有event, 然后消费; 这里的实现有点意思, 似乎是为了广度优先地处理一个线程发出的所有event; 可以想象一下用的Executor是directExecutor, 然后在subscriber里(处于同一个线程)又发出了event, 那么这时候这些event会等到当前event处理完才被处理
-
ImmediateDispatcher: 立即处理, 深度优先, 但目前并没有被代码用到, 用于可见性也无法用到它
-
LagacyAsyncDispatcher: 异步dispatch, 但为什么是Lagacy??? 不推荐了吗?
-
同一个线程发出的多个消息的投递顺序不能保证, 因为是放入线程池执行的, 后放的可能比先放的跑
-
去看它的代码, 很好理解, 就是将每个subscriber调用event的这个动作完全打散
-
Subscriber
负责调用被注解的方法, 如果订阅的方法被AllowConcurrentEvents注解标记了, 则认为该方法是线程安全的, 可以被并发调用. 否则会在调用目标方法时synchronized
关键字锁住当前Subscriber
以保证只有一个线程在调用.
AsyncEventBus
异步的EventBus, 仅仅只是EventBus的参数定制版.
实现里的一些优化点
-
拼接迭代器, 而不是返回全部元素组成的List, 这样对内存很友好
-
获取事件类型的所有父类
例子
public class EventBusTest {
@Test
public void test_sync() {
// 采用默认配置
final EventBus b = new EventBus();
final Subscriber subscriber = new Subscriber();
b.register(subscriber);
// 事件类型是 String
// 关联的Subscriber会在post方法会在当前线程上执行
b.post("aa");
assertThat(subscriber.received).isTrue();
subscriber.received = false;
b.post(1);
assertThat(subscriber.received).isTrue();
}
public static class Subscriber {
private boolean received;
@AllowConcurrentEvents
@Subscribe
public void onEvent(String str) {
System.out.println("String Event " + str);
received = true;
}
@AllowConcurrentEvents
@Subscribe
public void onEvent(Integer integer) {
System.out.println("Integer Event " + integer);
received = true;
}
}
}