记得上次写EventBus还是在上次(一年前,哈哈),转眼间又是一年了,发现对于EventBus的源码细节有点模糊,挖个坑捋捋EventBus的源码
由于项目中使用且当前最新版本源码变化不大,本文贴出的源码基于EventBus3.0.0,关于EventBus的用法可以移步我之前的文章:
Android 轻量级线程间通信EventBus
Android EventBus保姆级源码解析(一)注册方法register
Android EventBus保姆级源码解析(二)发送事件post
Android EventBus保姆级源码解析(三)黏性事件原理
先上总结(太长不看版):
EventBus.register
方法开始订阅,先通过反射获取注册类中使用@Subscribe
注解的方法EventBus.subscribe
方法添加到subscriptionsByEventType
列表中(<订阅class,<订阅类,订阅方法>>的一个Map)首先,EventBus的原理是基于发布订阅模式,有些萌新开始会以为是观察者模式,其实它跟观察者模式的区别在于不需要观察者(订阅者)和目标(发布者)直接交互,而是通过调度中心来进行分发事件,实现了它们之间的解耦。
看源码首先可以从我们使用的第一步,注册订阅事件的方法register
开始:
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
其中
SubscriberMethodFinder
是EventBus用来管理订阅事件的方法类SubscriberMethod
是EventBus的订阅方法(使用@Subscribe注解的方法),其中封装了订阅者的回调方法、线程模式、EventClass、是否为粘性事件等信息EventBus通过传入的Object对象subscriber
的class,构造出了一个SubscriberMethod
数组,看看构造数组的findSubscriberMethods
方法中有什么
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//如果缓存里有的话直接使用
if (subscriberMethods != null) {
return subscriberMethods;
}
//针对是否忽略继承的索引,采用不同的查找策略
//通过反射获取Subscriber注解的方法list,这块就不往下跟了,各位有兴趣可以自己看看
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 {
//如果构造出的subscriberMethods不为空,则放入缓存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
简单说,findSubscriberMethods
方法中主要是通过反射获取目标类中添加了@Subscriber注解的方法数组
然后register()
中会遍历方法数组,调用subscribe
方法订阅:
其中
Subscription
是包裹订阅类和订阅者信息的包装类SubscriberMethod
是上边说过的EventBus的订阅方法,其中封装了订阅者的回调方法、线程模式、EventClass、是否为粘性事件等信息,为了防止各位弄混,再强调一下typesBySubscriber
是EventBus中存放的“订阅者-事件类型”列表subscriptionsByEventType
是<订阅class,<订阅类,订阅方法>>的一个set列表 // Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//获取事件类型(EventClass)对应的订阅(订阅者,订阅者方法)list
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//指定的事件类型没有对应的观察对象的时候,初始化list再添加进去
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果同一个类重复订阅同一个事件,抛出EventBusException异常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
//遍历subscriptions,依据优先级,将新的订阅插入到list中
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);
//如果是订阅者的第一个订阅事件,构造一个typesBySubscriber put之后再添加
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//如果这个事件是黏性事件
if (subscriberMethod.sticky) {
//是否判断同类型的超类flag
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>).
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();
// post黏性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
// 从黏性事件list中取出事件
Object stickyEvent = stickyEvents.get(eventType);
// post黏性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
对于普通事件,添加到subscribedEvents
后该时间就处理完毕了,对于黏性事件,则还会使用checkPostStickyEventToSubscription(newSubscription, stickyEvent);
方法判断是否向该观察者post黏性事件
等等,我们的标题是什么,EventBus的register
,注册事件,为什么注册事件会进行事件的分发,这个就是黏性事件的机制了,这个问题我们后面在黏性事件的一篇再分析,我们简单看一下里面的代码
//跳过stickyEvent为空的情况
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
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 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);
}
}
到这里就是该post处理事件的逻辑了
到这里本篇EventBus.register
注册方法的解析就结束了,详细的代码解析会放到后续的处理post事件解析