Eventbus 3.1.1 源码解析(二)
接着上篇文章,继续对Eventbus的源码进行解析
3.3 EventBus post(Object subscriber)
在介绍基本使用时,我们已经知道了发送事件是使用post(Object subscriber),接下来我们来分析一下它的源码:
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
//得到当前线程的postingState
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<PostingThreadState> currentPostingThreadState获取当前的postingState,关于ThreadLocal,它是一个线程内数据存储类,它的作用域只在线程内部,支持在指定线程内进行存储和读取数据,Android消息机制中,MessageQueue就是使用ThreadLocal来存储数据。
因此,通过获取到的postingState就是当前线程保存的部分数据,参数如下:
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
接着,我们来看下发送单个事件的方法postSingleEvent(eventQueue.remove(0), postingState) 是如何实现的:
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);
//下面这句代码的理解是,只要有一个返回true,subscriptionFound就为true
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//如果没有找到订阅者,则根据初始的配置,判断是否进行日志信息打印和NoSubscriberEvent事件发送
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));
}
}
}
接着我们继续看下发送方法postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)的实现:
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 = 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;
}
上面的方法就是通过找到事件的订阅者,然后向每个订阅者进行事件发送,如果成功发送则返回true,没有找到订阅者或者事件被取消则返回false。
接着,我们继续看下事件是如何传递给订阅者的,
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);
}
}
通过订阅事件时配置的五种不同的线程模式,对事件分别进行不同的处理:
- 1、POSTING
订阅事件与发送事件在同一线程。对线程无限制,但是如果发送post线程为主线程,则订阅方法不能有耗时的操作,适用于耗时少、对线程无要求的场景; - 2、 MAIN
必须在主线程执行。如果当前线程是主线程,则直接执行订阅方法,如果不是,则通过Hanlder发送消息到主线程,再执行订阅方法,适用于UI绘制或者耗时少等必须在主线程执行的场景。 - 3、 MAIN_ORDERED
跟MAIN一样,都是在主线程执行。区别在于,它没有先判断发送事件的线程是否为主线程,直接通过Hanlder发送消息再调用主线程执行订阅方法。 - 4、 BACKGROUND
在后台线程执行。如果发送事件的线程在主线程,调用子线程去执行,否则直接在当前线程执行订阅方法。这与直接通过异步线程去执行的区别就是,后台线程backgroundPoster只有一个,如果事件过多,backgroundPoster里面维护了一个发布队列,依次执行事件,但这也导致了执行过程中,其他订阅方法的响应会一直处于等待中的状态。因此,如果方法执行耗时、执行过于频繁,都会对其他订阅方法的执行造成影响。所以这种模式适用于轻微耗时、执行不会过于频繁的场景。 - 5、 ASYNC
异步执行。直接调用异步线程执行,不存在其他方法的执行处于等待的现象,适用于耗时操作。
最后,我们看下启动订阅方法执行的invokeSubscriber(Subscription subscription, Object event)
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);
}
}
通过反射找到订阅者方法,然后传入对应的事件执行订阅逻辑,到此,发送事件的流程也解析完毕,整体流程如下:
post-flow-chart.png
3.4 unregister(Object subscriber)
最后,我们看看整个流程的最后一步,解除注册unregister(Object subscriber)的解析:
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
//通过订阅者找到订阅的事件类型list
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());
}
}
具体移除订阅者和事件关联的逻辑如下:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
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 == s ubscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
上面的解除注册方法就是找到订阅者订阅的所有事件,依次遍历,移除每个事件与订阅者的关联,最后把订阅者对应订阅事件的缓存也清除,防止内存泄露的发生。
至此,Eventbus的源码已经分析得差不多,后面对分析它整体的设计,思考我们可以学习和借鉴的地方。