相信大家都有在项目中使用过EventBus。EventBus是一个性能高效的基于观察者模式的事件发布与订阅框架。借助EventBus,我们只需几行代码便能实现组件之间、线程之间的通信,达到解耦的目的。
这篇文章不对EventBus的使用进行介绍,而是格物致知,探究EventBus的源码。
1. 入口
一般使用时是通过EventBus.getDefault()来调用注册、发布与注销的方法。因此,我们从getDefault()这个入口开始分析。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
这里使用了双重检验锁(单例模式的一种实现方式)的来保证EventBus实例的唯一性。接着进入构造函数看看:
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
在无参构造函数内调用了带有EventBusBuilder参数的构造函数,这里使用了Builder模式,将参数的构建过程交给EventBusBuilder。Builder模式是一种常见的设计模式,例如在sdk开发中参数过多时,将参数的构建过程交给Param:
Param param = Param.instance().param1(value1).param2(value2)......;
SDK.init(context, param);
在EventBus的构造函数内,进行了初始化的工作。这些参数的含义对后面的分析很重要,这里简单介绍下这些属性的作用:
- subscriptionsByEventType:以EventType(Event对象的Class类)为key,Subscription(订阅者以及一个订阅方法)数组为value。根据EventType可以查询到相应的订阅者及订阅方法。
- typesBySubscriber:以Subscriber(订阅者,即调用register方法的类)为key,EventType数组为value。根据订阅者可以查询到它订阅的所有事件。
- stickyEvents:以EventType为key,Event为value。用来保存粘性事件。
- mainThreadPoster、backgroundPoster、asyncPoster:EventBus支持四种ThreadMode,POSTING、MAIN、BACKGROUND、ASYNC。POSTING直接在发布事件的线程处理,这三个poster分别用来支持剩下的三种Mode。
- indexCount:索引类数目,索引类指的是EventBus利用注解处理器生成的保存订阅者信息的类。如果lib中也用了EventBus,就可能存在多个索引类。
- subscriberMethodFinder:查找及缓存订阅者信息的类。
- logSubscriberExceptions:当事件处理过程发生异常时是否打印日志,默认为true。
- logNoSubscriberMessages:当事件没有订阅者订阅时是否打印日志,默认为true。
- sendSubscriberExceptionEvent:当事件处理过程发生异常时是否发送SubscriberExceptionEvent,默认为true。当为true时,订阅者可以订阅SubscriberExceptionEvent事件。
- sendNoSubscriberEvent:当事件没有订阅者订阅时是否发送NoSubscriberEvent,默认为true。当为true时,订阅者可以订阅NoSubscriberEvent事件。
- throwSubscriberException:当事件处理过程发生异常时是否抛出EventBusException,默认为false。
- eventInheritance:是否支持事件继承,默认为true。当为true时,post一个事件A时,若A是B的子类或者A实现了接口B,订阅B的订阅者也能接收到事件。
- executorService:线程池,负责线程调度。
因此,通过EventBus.getDefault()我们就可以得到一个默认配置的EventBus单例,也支持通过EventBusBuilder来自定义配置。
2. 注册订阅者
接下来看下通过EventBus.getDefault().register()注册订阅者时发生了什么?
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的findSubscriberMethods来获取SubscriberMethod数组,SubscriberMethod保存了订阅方法的信息。打开SubscriberMethod可以发现一点,在SubscriberMethod有Method对象以及methodString,其实methodString也是通过method反射获取到的,这里利用一个变量保存起来,避免每次都通过反射获取降低性能。类似的,在EventBus其实还有很多这种细微的优化。
得到SubscriberMethod数组后,依次进行注册。先看看获取到SubscriberMethod数组后,是如何通过subscribe进行订阅的:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> 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<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 粘性事件处理, 将在第五小节分析
}
3-30行容易理解,主要是将EventType -> Subscription的映射加入到subscriptionsByEventType,将Subscriber -> EventType的映射与加入到typesBySubscriber中。subscriptionsByEventType里每个事件的Subscription是按照优先级排序的,优先级高的订阅者可以中途通过cancelEventDelivery来拦截。
31行后面部分的代码部分涉及到粘性事件,将在第五小节中分析。
回过头看看findSubscriberMethods是如何获取到SubscriberMethod数组的:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> 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;
}
}
在findSubscriberMethods方法内,如果METHOD_CACHE中没有的话才进行查找的工作。然后根据ignoreGeneratedIndex判断查找的方式。
EventBus3.0里使用了一项“秘密武器”使得效率大大提升,这个武器其实就是之前文章里提过的注解处理器。如果ignoreGeneratedIndex是false的话,就采用注解处理器生成索引类去获取SubscriberMethod;否则采用反射的方式。
- butterknife源码分析:如何处理注解—反射与注解处理器
http://blog.csdn.net/u012933743/article/details/54972050
2.1. 索引获取SubscriberMethod
ignoreGeneratedIndex默认为false,因此会调用findUsingInfo:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}