前言
EventBus已经使用了那么久啦 ,但是一直都没有去了解过其中内部的机制,后来虽然看了一些博客,但是介绍的内容虽然看过了,但是还是不能很清晰的知道内部实现原理。所以本着 纸上得来终觉浅 绝知此事要躬行 的原则,决定亲自去看一看源码。
内容部分
注册方法分析
//注册的类的注册的事件的方法集合
private final SubscriberMethodFinder subscriberMethodFinder;
//注册方法,这里会存储你注册的对象,里所有的方法(注册监听的方法)
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//这里是以类为key获取所有该类里的方法。(注册监听的方法)
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//对所有的方法遍历进行subscribe(建立订阅)
subscribe(subscriber, subscriberMethod);
}
}
}
这里遍历订阅过程需要在同步锁里进行,为了防止重复注册,导致消息接收重复。
//SubscriberMethod 类记录了订阅方法的信息
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
subscribe方法内进行一系列的操作,这里主要的工作如下:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//事件类型,应该解释为事件类型。 这个类型是你注册的方法里的参数类
Class<?> eventType = subscriberMethod.eventType;
//这里是把方法和方法的持有对象关联起来
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//通过类名字的这个key获取所有该类下的方法,如果没有见加入到map中
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
//每个方法都放到map中
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);
//是粘性么?
if (subscriberMethod.sticky) {
//继承关系的要处理父类和子类
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
//粘性事件map
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);
}
}
}
以上就是注册方法
总结出来:
-
获取注册类下所有的注册事件的方法,如activity注册了event的监听,就是获取到所有该activity下添加注解标记的方法。
-
将每个方法放到不同的列表中进行存储,存储的为map。key为事件类型,value为所有注册该事件的方法的包装类(Subscription)。类中中包含了一些必要信息。如该方法的持有者。(将注册了EventBus的类里注册不同的EventType的方法放到不同的map中存储。key为EventType,value为一个Subscriber的列表)
-
后续对事件的优先级进行调整,就是调整事件在list中的index
-
将类中的所有注册的事件也统计起来,key为类,value为里面的事件集合,typesBySubscriber这个map中是存储了每个Subscriber中订阅的EventType类型
-
粘性事件单独存储,不做过多介绍
以上为注册eventbus所做的工作,
这里留一个问题,这个注册方法调用时机是如何?是在类初始化的时候调用?
普通事件发送流程分析
post是发送事件的入口,所以开始从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 {
//类似handler的loop机制,无限的取消息
while (!eventQueue.isEmpty()) {
//一直从0位置开始取消息
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
//异常就切换线程,取消发送
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
上面的PostingThreadState记录一些常用的状态内容。
主要内容是发送的内容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++) {
//拿到注册该post事件类的字节码了
Class<?> clazz = eventTypes.get(h);
//调用post方法,找到接收该事件的订阅者了
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//调用post方法,找到接收该事件的订阅者了
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()方法,这里会返回消息发送成功还是失败。Subscription是比较关键的信息类,里面记录了注册了该事件的所有的类,为后续我们去挨个调用注册类使用。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//通过类的字节码,拿到所有的注册事件的类,解释一下,通过你发送的一个event事件,来拿到所有注册了这个事件的类
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);
//默认取消发送为false
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
这里的一系列操作继续向下调用postToSubscription方法,这个方法比较简单,主要是根据事件的所处于的线程来进行不同的分发。代码如下
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//直接就调用方法了,当前线程
invokeSubscriber(subscription, event);
break;
case MAIN:
//主线程的调用,如当前线程不是,需要切换线程(通过handle来做)
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
//有序的,通过handle来做
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
//后台的,通过Runnable来实现的
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
//异步的,通过Runnable来实现的
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
上面代码分的类型比较多,但实际上划分只有两种,一种是常规的发送,一种是特殊处理的发送。特殊处理包括主线程,异步线程,后台线程等。
这里我们可能遇到过一个问题,就是在自线程中发送的消息,如果不在接受的地方表明主线程,会报错。
重点来了啊,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);
}
}
结束啦
上面这些关键代码,就完成了一次普通事件的分发流程,其中没有过多介绍细节的实现。
至于粘性事件分发,和普通事件,就差了一个收到事件后需要通知队列,因为如果没有通知,队列的内的粘性消息会一直处于发送状态。