该篇我将继续讲解EventBus事件发送,调用和解绑,接下来进入今天的源码解析之路。
EventBus.getDefault().post(new TestEvent("hello Eventbus"));
EventBus.getDefault().postSticky(new TestEvent("hello sticky Eventbus"));
EventBus发送事件有两种方式,分别为post和postSticky,首先从post开始:
/** Posts the given event to the event bus. */
/**
*发送事件第一步:发布给定的事件到事件总线
*/
public void post(Object event) {
//获得一个PostingThreadState对象
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;
}
}
}
通过ThreadLocal.get()获取一个PostingThreadState对象,PostingThreadState是EventBus的一个内部类,用来判断一些订阅的操作,比如是否发送,是否在主线程等。调用eventQueue.add(event)将事件添加到集合中,postingState.isPosting判读该事件是否已经发送,若没有则继续执行,若集合不为null,则执行postSingleEvent方法,传递事件和postingState对象。finnally中对postingState对象的参数进行重置,对象进行重用。
/**
*发送事件第二步:循环遍历订阅参数及其超类添加到集合中
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//获取订阅方法参数类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//默认eventInheritance=true 执行这里
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);
}
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));
}
}
}
根据发送的事件获取订阅参数类型,调用lookupAllEventTypes方法获取所有关联的订阅参数,这里的集合包含了父类和接口等。集合循环遍历每一个订阅参数类型,调用postSingleEventForEventType方法继续执行。
/**
*发送事件第三部:根绝订阅参数类获取订阅者集合,遍历调用订阅类的订阅方法
*/
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
//根据订阅参数获取subscriptions列表
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//循环遍历每一个subscription对象,调用其中订阅类的订阅方法
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
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;
}
根据传递的订阅参数从subscriptionsByEventType集合中获取Subscription集合,循环遍历每一个Subscription对象,对postingState一些对象进行赋值,然后调用postToSubscription调起具体的订阅方法。finally中对postingState的变量进行重置。这里的返回值表示的是否找到订阅方法,若返回 false,并不会报错,需要注意下。我们也可以看到postingState对象主要就是用于对一些状态的判断,以及传递事件是否在主线程执行。接下来看一下具体的代码。
/**
*根据订阅方法的threadMode,在不同的线程调起订阅方法
*/
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:
//与MAIN的区别在于,MAIN_ORDERED会直接入队,有序排队等待处理,
//若post在主线程,则直接掉起订阅方法,不入队,若post在子线程,则入队处理
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);
}
}
在这里要根据设置的threadMode来决定最终在哪里调起订阅方法,POSTING代表在哪里发送就在哪里调用,所以这里直接调用invokeSubscriber方法。MAIN和MAIN_ORDER的区别主要在于:若是在子线程中发送事件,则两者是一样的都会调用mainThreadPoster执行Handler的事件处理操作,若是在主线程中则MAIN会直接调用invokeSubscriber方法,不需要执行入队操作。至于BACKGROUND和ASYNC都在子线程中调起订阅方法,不同的在于后者是不同步的。
eventBus.invokeSubscriber(pendingPost);
/**
*通过Method反射invoke(调用)订阅类中的订阅方法
*/
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);
}
}
最终通过反射来调起订阅方法,接下来看一次postSticky的操作:
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
会发现首先将粘性事件存储到stickyEvents集合中,那么当我们注册粘性事件的时候,就会从该集合中查找看是否有符合的事件,其余的操作与post一致,不再讲解。
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());
}
}
/**
*根据订阅类和订阅参数类型移除数据
*/
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--;
}
}
}
}
这里首先根据解绑的订阅类从typesBySubscriber集合中获取订阅参数集合,然后调用unsubscribeByEventType方法根据订阅参数查找到所有符合条件的订阅类集合,从中移除需要解绑的订阅类,最后根据订阅类从typesBySubscriber集合中移除 所有属于该类的订阅参数数据。可以看到解绑还是比较简单的。
到这里关于EventBus的源码解析就差不多了,后面有时间还会出一些关于3.0后的订阅下标的使用和在源码的理解中学到了什么。避开误区,了解原理以及借鉴其设计模式和思想