一起养成写作习惯!这是我参与「掘金日新计划 · 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在Eventbus3.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获取订阅事件的类型eventType,也就是@Subscribe注解修饰方法的参数类型,然后将订阅者subscriber和订阅方法subscriberMethod封装在Subscription实例中,然后通过订阅事件类型eventType从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后,后续的订阅方法将不再执行,这也意味着后面的订阅者不会收到消息。