要理解EventBus是如何运行的,就得先知道其是如何使用的
EventBus.getDefault().register(this)
EventBus.getDefault().post(new Event())
这里的事件是自定义的,可根据实际的数据需求创建不同的构造函数从而实现数据的传递
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(Event event) {
//处理逻辑
}
注意这里事件的处理方法必须加上Subscribe注解,否则无法找到,具体的原因后续进行分析的时候再进行说明
首先,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在进行注册的时候到底干了什么事情勒,源码如下:
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
注册的时候传入了this作为参数,也就是注册所在的类作为subscriber,首先,会获取到注册者的class,随后根据该class类型,查找该宿主的哪些方法订阅了事件进行消费,具体的查找方式为
List findSubscriberMethods(Class> subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
这里存在两种查找的方式,主要是EventBus这里进行了优化,首先看第一种,从名字就可以看出是通过反射的方式进行查找
private List findUsingReflection(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
这里值得一提的是FindState的创建,调用prepareFindState()方法,采用享元设计模式增加缓存来减少对象的创建,因为可能存在多个方法进行消费,所以这里也是为了节约内存
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
这里的FIND_STATE_POOL是一个数组,最大为4,继续上面通过反射对订阅方法进行查找方法消费,最终是调用到findUsingReflectionInSingleClass(FindState findState)方法,看看具体做了啥
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
try {
methods = findState.clazz.getMethods();
} catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
String msg = "Could not inspect methods of " + findState.clazz.getName();
if (ignoreGeneratedIndex) {
msg += ". Please consider using EventBus annotation processor to avoid reflection.";
} else {
msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
}
throw new EventBusException(msg, error);
}
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
可以看到,这里通过反射的方式拿到了订阅者的所有方法,并找到带有Subscribe注解的方法,拿到所有的其所订阅的事件类型和线程模式,并创建SubscriberMethod最终保存在findState.subscriberMethods中进行返回,可以继续看到findUsingReflection方法中的这段逻辑
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
就是这里不仅会找传入的订阅者注册的一些方法,还会找出其父类订阅的一些方法进行返回,最终,找到的订阅方法会以subscriberClass, subscriberMethods键值对的形式放入METHOD_CACHE中,保证了线程的安全
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
第二种说的查找的方法就是调用findUsingInfo方法,这里的info便是SubscriberInfo或者是subscriberInfoIndexes进行查找,可以看到SubscriberInfo为一个接口类型,也就是需要我们自己去实现,然后实现getSubscriberMethods()方法,也就是我们将订阅相关的信息,告诉EventBus,然后EventBus通过该信息直接找到订阅者相关的方法
同样,通过该方式找到后的subscriberMethods也存放在METHOD_CACHE中进行保存,上述只是找到了订阅者消费事件的方法,接下来则是进行订阅
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
这是register方法中的一段代码,就是订阅的逻辑,这里对每个找到的方法进行订阅,接下来看看是如何进行订阅的
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList 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> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
}
可以看到,这里首先获取了消费事件的方法所消费的事件类型eventType,每一个Subscription是一个订阅者-订阅方法的实体类,subscriptionsByEventType则是一个Map,其是以
eventType - CopyOnWriteArrayList为键值对的数据格式,这里分析到,这里的subscriptionsByEventType以事件类型作为key,保存了订阅该事件的订阅者以及消费该事件方法相关的信息。接下来看订阅过程中的一段代码
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;
}
}
结合上面完整的代码看,subscriptions为一个事件类型对应的订阅者相关的信息,newSubscription为一个新的订阅者相关的信息,这里便是对一个新的订阅者信息处理的过程,这里巧妙的i== size判断了两种情况:
这里对newSubscription进行了一个饿优先级的排序,继续看后面的处理
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
typesBySubscriber是一个订阅者- 事件类型列表的键值对的Map,也就是说这里维护了一个订阅者所订阅的所有事件类型的Map,供后续进行使用
到这里,注册便已完成,接下来是事件的发送以及相关的消费
通过上面的使用可知,事件的发送是通过EventBus的post方法,那么是如何实现的呢?还是看下源码就知道啦
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
这里首先通过ThreadLocal拿到当前线程的posting状态,其包含了事件队列,以及订阅者相关的信息,这里用ThreadLocal也保证了一个线程只有一个事件队列等状态信息
final static class PostingThreadState {
final List
首先将要发送的事件添加到该线程事件队列中去,并通过轮询的操作将队列中的事件进行post,接下来看看postSingleEvent做了什么事
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List> 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));
}
}
可以看到,这里也有个分支,是通过eventInheritance进行控制的,具体多实现的步骤就是在lookupAllEventTypes方法中
private static List> lookupAllEventTypes(Class> eventClass) {
synchronized (eventTypesCache) {
List> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
这里拿到了该事件类型的所有超类或者间接超类的事件类型,存放在list中,随后便通过遍历拿到事件进行post,而没有eventInheritance时则是直接将该事件进行了post,具体的实现在
postSingleEventForEventType中
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList 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;
}
这里首先通过事件的类型拿到所有的订阅者,如何拿到的呢?就是subscriptionsByEventType,前面我们所说的以事件类型 - 订阅者信息为键值对的map,这个map存贮了这样的对应关系,随后便是熟悉的轮询将事件post到对应的订阅者
这里有个小插曲,我们看到这里有个abort,能将当前的事件的post给停掉,我记得当时面试的时候有个面试官面试过我一个关于EventBus的源码问题,说当我post一个事件,订阅者有很多,我只想让那个A、B两个收到我的事件,该如何做,我当时还傻傻的想这如何实现,类似广播截断,其实现在想,面试官其实还是想考我源码,这里的abort其实就能实现,具体的做法就是:记得之前在保存订阅者信息的时候有个subscriptions,在添加newSubscription的时候,会根据优先级进行排序,因此这里配合优先级以及Abort,实现的方法就来了;将A、B的消费事件的方法优先级设置为较高,放在队首,当A、B中的优先级较低的一个处理完事件后,调用EventBus的cancelEventDelivery方法,就能将该事件的传递终止;所以其实还是考的对源码的理解深度
事件的最终消费是通过,其消费的方式也是通过我们消费方法限定的线程模式来进行区分的,具体的线程模式在文章前就进行了描述,这里以MAIN线程模式的进行分析,其他的也就是大同小异了
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;
...
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
这里首先通过isMainThread来进行判断当前的post的线程是否是主线程,由于事件消费的规定在主线程,因此这里可以直接调用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);
}
}
如果post线程不是主线程,那么肯定得进行线程的切换,mainThreadPoster.enqueue(subscription, event)就是相应的实现,这里的mainThreadPoster是在创建EventBus的时候创建的
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
具体的实现类就是AndroidHandlerMainThreadSupport,创建的Poster其实就是HandlerPoster,其是Handler的子类,所以这里的mainThreadPoster.enqueue其实就是可以看作是Handler.sendMessage的作用,只不过这里包装了一层,在enqueue的时候,将事件和订阅者封装成PendingPost,放进PendingPostQueue队列中,随后便调用sendMessage方法,并在handleMessage中进行处理,通过从PendingPostQueue队列取出PendingPost,随后调用EventBus的invokeSubscriber(PendingPost pendingPost)重载方法,并最终还是调用invokeSubscriber(subscription, event)进行反射后调用订阅者的消费方法进行消费
整个流程就走通了
加入存在这样的一种情况,当事件发送的时候,订阅者还未进行注册,此时订阅者该如何接受到该事件呢?sticky帮你解决这个问题
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
在Subscribe注解中还包含了一个sticky,那么该注解是如何帮我们解决这类问题,达到粘性事件的目的呢?
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);
}
通过postSticky的方式,将事件post,由于此时订阅者还未注册,因此,这里就没那么多麻烦事了,先是将该粘性事件保存在stickyEvents这个map中,随后直接post事件,其流程和普通流程的post一致,会一直走到postToSubscription随后调用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);
}
}
但是这时候有个问题,这里没有订阅者,因此该invloke方法就会调用失败,从而会catch住,调用handleSubscriberException方法
private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
if (event instanceof SubscriberExceptionEvent) {
if (logSubscriberExceptions) {
// Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log
logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
+ " threw an exception", cause);
SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in "
+ exEvent.causingSubscriber, exEvent.throwable);
}
} else {
if (throwSubscriberException) {
throw new EventBusException("Invoking subscriber failed", cause);
}
if (logSubscriberExceptions) {
logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
+ subscription.subscriber.getClass(), cause);
}
if (sendSubscriberExceptionEvent) {
SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
subscription.subscriber);
post(exEvent);
}
}
}
这里会创建一个SubscriberExceptionEvent进行post随后进行打印,这里是EventBus的异常Case的情况,真正sticky实现还是在register方法里面,在post的时候,我们保存了该粘性事件,而在register的时候
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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去拿,如果存在则直接调用postToSubscription方法让订阅者消费该事件,来完成在注册的第一时间就让订阅粘性事件的订阅者消费改事件
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}