一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
创建
一般情况下,我们使用EventBus.getDefault()
获取EventBus
实例,它采用了
标准的双重锁检查的单例模式。
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
此外,EventBus
也给我们提供了通过构造者获取实例的方法,首先通过EventBus.builder()
获取EventBusBuilder
类的实例,然后自定义参数获取EventBus
实例。
EventBus eventbus = EventBus.builder()
.logNoSubscriberMessages(true)
.sendNoSubscriberEvent(true)
.build()
在通过单例模式获取实例中,内部最后也是通过构造者模式获取的实例,他使用了一个默认的builder。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBusBuilder主要提供了以下参数供我们使用
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean eventInheritance = true;
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
Logger logger;
MainThreadSupport mainThreadSupport;
EventBusBuilder() {
}
}
logSubscriberExceptions
当订阅者的事件处理方法内部发生异常时,是否打印日志。logNoSubscriberMessages
当没有注册处理当前事件的订阅者时,是否打印日志。sendSubscriberExceptionEvent
与logSubscriberExceptions
相似,当订阅者的事件处理方法内部发生异常时,是否发送SubscriberExceptionEvent
消息。sendNoSubscriberEvent
与logNoSubscriberMessages
相似,当没有注册处理当前事件的订阅者时,是否发送NoSubscriberEvent
消息。throwSubscriberException
与logSubscriberExceptions
相似,当订阅者的事件处理方法内部发生异常时,是否抛出EventBusException
异常。eventInheritance
事件是否支持继承,如果支持,那么在发送子事件的时候,订阅了父事件的订阅者也能收到此消息。ignoreGeneratedIndex
在Eventbus
3.x版本中,查找订阅者的订阅方法有两种方式,一是运行时反射获取,另一个是通过编译时注解获取,默认为后者,可以将此参数改为true强制使用反射。strictMethodVerification
是否使用严格检查,开启之后如果遇到非法的订阅方法会抛出EventBusException
异常,主要包括@Subscribe修饰的方法只能有一个参数,且此方法必须是public
,非static
,非abstract
的。
注册
调用register()
方法注册订阅者,源码如下:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
首先获取订阅对象的Class
,然后通过SubscriberMethodFinder
类查找所有的订阅方法并封装在SubscriberMethod
中,SubscriberMethod
包含订阅方法的关键信息,比如线程模型,优先级,参数,是否为黏性事件等,findSubscriberMethods()
方法返回的是一个List
,说明一个订阅者可以订阅多种事件。
订阅
查找完成之后,通过subscribe()方法完成订阅。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
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;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//......粘性事件处理
}
- 这里用到了两个
Map
,subscriptionsByEventType
是Map<Class<?>, CopyOnWriteArrayList<Subscription>>
类型,key
为eventType
,value
是订阅信息集合,typesBySubscriber
是Map<Object, List<Class<?>>>
类型,key
为订阅者类,value
是eventType
集合。 - 首先,通过
SubscriberMethod
获取订阅事件的类型eventTyp
e,也就是@Subscribe
注解修饰方法的参数类型,然后将订阅者subscriber
和订阅方法subscriberMethod
封装在Subscription
实例中,然后通过订阅事件类型eventTyp
e从subscriptionsByEventType
中获取订阅信息集合,如果该集合为空则创建并加入subscriptionsByEventType
,否则判断此集合是否包含最新的newSubscription
,如果包含则抛出异常(说明一个订阅者不能重复订阅相同的消息类型),之后按照订阅方法的优先级priority
吧最新的订阅信息加入集合。 - 然后通过订阅者
subscriber
将它所有的订阅事件类型都保存在typesBySubscriber
中
粘性事件处理
完成以上动作之后,如果订阅事件是粘性事件,则还需要额外处理
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);
}
}
stickyEvents
是Map<Class<?>, Object>
类型,key
是粘性事件类型,value
是我们通过postSticky()
发送的粘性事件- 如果
eventInheritance
为true
,也就是事件支持继承,那么先遍历stickyEvents
,找到当前事件类型的子类型,然后调用checkPostStickyEventToSubscription()
发送订阅信息,如果不支持继承,则直接发送。
取消注册
注册流程中主要做了三件事
- 建立订阅事件类型
eventType
和订阅信息(包括订阅者subscriber
和订阅方法subscriberMethod
)的映射,保存在subscriptionsByEventType
中 - 建立订阅者
subscriber
和他订阅事件类型eventType
的映射,保存在typesBySubscriber
中 - 粘性事件处理
除了第三件事,注册流程都是在建立映射关系,取消注册主要就是移除这类关系
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());
}
}
首先通过typesBySubscriber
获取订阅者subscriber
的订阅方法subscribedEvents
,如果不为null
,遍历调用unsubscribeByEventType()
方法,然后将订阅者subscriber
从typesBySubscriberMap
中移除。
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
通过订阅事件类型eventType
从subscriptionsByEventType
中找到订阅信息,然后遍历订阅信息,找到需要移除的(subscription.subscriber == subscriber
)信息,先把其active
标志位false
(后续不再处理),随后移除。因为在遍历中移除元素,所以subscriptions
定义为CopyOnWriteArrayList
。
发送事件
post
public void post(Object event) {
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;
}
}
}
PostingThreadState
是EventBus
为每个线程保存的线程变量,保证多个线程之前数据互不干扰,它有以下属性
eventQueue
保存事件的列表isPosting
是否正在发送事件isMainThread
是否在主线程canceled
是否取消了发送
首先从ThreadLocal
中获取PostingThreadState
,然后获取其eventQueue
并加入当前事件,随后用isPosting
确保当前线程没有正在发送事件,然后对isMainThread
和isPosting
赋值,判断canceled
确保能正常发送,最后循环eventQueue
,取出事件交给postSingleEvent()
处理,这里使用了try-finally
,保证了无论是发布事件出现异常还是事件发布完毕,都会对isPosting
和isMainThread
重新赋值,以便下次post
能正常进行。
postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//.....结果处理
}
postSingleEvent()
方法主流程比较清晰,首先获取事件类型,然后判断eventInheritance
,如果事件支持继承,则通过lookupAllEventTypes()
查找当前事件的所有的类对象,包括超类和接口,
然后遍历交给postSingleEventForEventType()
处理,如果不支持继承,则直接由postSingleEventForEventType()
处理当前事件。
postSingleEventForEventType()
方法返回处理结果,如果失败,进行异常处理
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
结果处理由logNoSubscriberMessages
和sendNoSubscriberEvent
决定,或打印日志,或发送NoSubscriberEvent
事件
postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
通过eventClass
从subscriptionsByEventType
中取出订阅信息subscriptions
,遍历这些信息将对应的值赋给postingState
的属性,然后交给postToSubscription
处理,在处理过程中,如果遇到异常,重置postingState
的属性,直接处理下一个,如果遇到取消发送(canceled
为true
),直接break
,停止处理
postToSubscription
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
Eventbus
的订阅方法可以定义线程模式,根据不同的模式执行不同的策略,但是不论那种策略,最后都会由invokeSubscriber
处理。
invokeSubscriber
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
直接从订阅信息subscription
中获取订阅方法,然后利用反射执行方法。如果遇到InvocationTargetException
异常,根据logSubscriberExceptions
和throwSubscriberException
参数决定是否打印日志,是否发送异常信息。
postSticky
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
}
粘性事件的发送与普通事件的发送基本相似,首先根据事件类型将当前事件保存在stickyEvents
中,然后调用post
直接发送事件,由于stickyEvents
是Map<Class<?>, Object>
类型,每次put
的时候如果key
相同,都会导致前一个value
被覆盖,所以同一事件类型EventBus
只是保留最新的。
取消事件
cancelEventDelivery
EventBus
支持在事件分发过程中取消事件
public void cancelEventDelivery(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
if (!postingState.isPosting) {
throw new EventBusException("This method may only be called from inside event handling methods on the posting thread");
} else if (event == null) {
throw new EventBusException("Event may not be null");
} else if (postingState.event != event) {
throw new EventBusException("Only the currently handled event may be aborted");
} else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
throw new EventBusException(" event handlers may only abort the incoming event");
}
postingState.canceled = true;
}
根据源码可以看出事件取消需要满足以下条件:
- 正在发送事件,即
postingState.isPosting==true
,一般情况下可以在订阅方法内部取消事件,这样就能保证isPosting
为true
。 - 不能取消空事件
- 取消的事件必须和正在发布的事件一样
- 只有线程模型为
ThreadMode.POSTING
才可以取消
当取消事件后,postSingleEventForEventType()
方法会获取到信息
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
可以看到当canceled=true
后,后续的订阅方法将不再执行,这也意味着后面的订阅者不会收到消息。