EventBus使用的是观察者模式,使用步骤主要分为三步:
1.订阅者注册
EventBus.getDefault().register(this);2.发布者发布事件
EventBus.getDefault().post(event);3.订阅者解除注册
EventBus.getDefault().unregister(this);
观看EventBus源码中,主要有两个Map十分重要:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
主要就是两个Map,剩余一个 stickyEvents 是粘性事件的Map,不管啦。
- subscriptionsByEventType:
该Map以EventType为key,Subscription数组为value,存储订阅者信息。暂且称之为注册Map。 - typesBySubscriber:
该Map以订阅者为key,EventType为value。主要用于解除注册使用。暂且称之为解注册Map
1.订阅者注册
/**
* 调用该方法注册
*/
public void register(Object subscriber) {
//1、获取订阅者的字节码文件
Class<?> subscriberClass = subscriber.getClass();
//2、获取订阅者里面所有的订阅方法(即使用了 @Subscribe 注解的方法)
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//3.将订阅者,订阅方法,EventType添加到两个Map中(即subscriptionsByEventType和typesBySubscriber)
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
主要来看看subscribe方法:
/**
* 必须在同步代码块中调用
*/
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//将订阅者,订阅方法封装成一个类(Subscription)
//Subscription只要在订阅者,订阅方法都相同的情况下才认为是相等的
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//以EventType为key,查找subscriptionsByEventType是否已经存在该Subscription。
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//以EventType为key,Subscription列表为value,存储到map集合中。注意当前Subscription列表是一个为空
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//禁止重复注册
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//如果当前列表已经存在,添加当前Subscription到列表中
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//将EventType列表为value,订阅者为key,存入到解注册Map中
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//粘性事件相关处理
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
注册过程大概就是这样。注册过程中,实际上就是往两个Map中填充数据的过程。
步骤是这样的:
- 调用 EventBus#register()
- register方法首先调用 SubscriberMethodFinder#findSubscriberMethods() 方法,找到当前订阅者里面所有的订阅方法,也就是加了@Subscribe 注解的方法啦。然后返回一个SubscriberMethod的集合subscriberMethods。
- register 方法再去遍历subscriberMethods,然后集合中的每个元素都调用EventBus#subscribe() 方法,将subscriber,SubscriberMethod与EventType进行关联。
而对于 EventBus#subscribe() 方法重点说明一下,主要分为以下步骤:
- 将 Subscriber, SubscriberMethod 封装成一个 Subscription 类
- 根据 EventType 查找 subscriptionsByEventType ,得到一个 Subscription 的列表,然后查看列表中是否存在当前的 Subscription 实例,不存在就放入列表中,存在就报错呗
- 同理,根据 subscriber 为 key, 查找 typesBySubscriber ,得到一个EventType 的列表,然后查找该列表是否存在该 subscriber, 不存在就直接存入表中,存在就报错。
2.发布者发布事件
/**
* 发布者通过此方法发布事件
*/
public void post(Object event) {
//从ThreadLocal中取出PostingThreadState,该对象中主要包含一个事件队列(eventQueue),并添加事件到队列中
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//依次遍历事件队列
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
主要就是先把发送的Event添加到队列中,然后开始遍历队列,调用postSingleEvent方法。
postSingleEvent就是进行一系列的处理,正常情况下就调用了postSingleEventForEventType方法。
postSingleEventForEventType方法主要作用就是根据Event,从注册Map中取出subscription列表,由前面的可以知道,subscription实际上就是subscriber与method封装而成的类。
所以,此时就知道了需要调用哪个类的哪个方法,接下来就是通过反射调用具体的方法咯。系统中调用的postToSubscription方法。
大致思路就是这样。
3.订阅者解除注册
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
实际上就是移除前面中两个Map中的Subscription。分为两步:
- 在typesBySubscriber中,根据subscriber找到该订阅者所有的订阅事件类型。即EventType列表
- 在subscriptionsByEventType,根据EventType列表,找到subcription列表,直接remove即可。
从解除注册就可以看到了,为啥需要两个Map去保存EventType,SubScriber,Method信息。