EventBus源码分析(一):构造及订阅
通过一文EventBus初步使用,了解了EventBus的使用。光了解是不够的,深入源码才算熟悉。EventBus源码不多,适合开发者精读学习,仅代码就1800余行(使用SourceCounter统计)。
1、构造EventBus实例
项目开发中,一般使用EventBus类中的getDefault()静态方法获取全局总线实例。
EventBus eventBus = EventBus.getDefault();
1.1、默认单例
getDefault()是标准的获取单例方法,获取EventBus库中实现的静态单例。关于单例详见单例的五种方式一文。
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
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;
}1.2、构造方法
用来构造EventBus的构造方法是这个无参构造方法,通过该默认构造方法也可以构造出一个EventBus实例,不过不推荐,应使用官方提供的getDefault()方法。
public EventBus() {
this(DEFAULT_BUILDER);
}默认构造方法中使用默认的EventBusBuilder构造器构造EventBus实例。注意到这个构造方法默认包访问权限。
开发者也就不能直接通过EventBus构造函数设置EventBusBuilder,并作为参数构造EventBus。有没有方法自定义设置EventBus呢?有的,继续看下面1.3节。
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
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;
}1.3、建造者 EventBusBuilder
EventBusBuilder也不是公开的构造方法。开发者不能直接通过构造方法创建EventBusBuilder实例。
EventBusBuilder() {
}EventBus类中有默认的EventBusBuilder构造器DEFAULT_BUILDER用来创建默认的EventBus实例。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
但是,在EventBus类中却提供的静态方法可以创建并获取EventBusBuilder实例:
public static EventBusBuilder builder() {
return new EventBusBuilder();
}通过建造者模式配置各种EventBus特性配置。建造者模式也是很多开源库里经常用到的设计模式之一,比如OkHttp中Request请求的构造就是通过建造者来配置构造请求。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool(); boolean logSubscriberExceptions = true; boolean logNoSubscriberMessages = true; boolean sendSubscriberExceptionEvent = true; boolean sendNoSubscriberEvent = true; boolean throwSubscriberException; boolean eventInheritance = true; boolean ignoreGeneratedIndex; boolean strictMethodVerification; ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE; List<Class<?>> skipMethodVerificationForClasses; List<SubscriberInfoIndex> subscriberInfoIndexes; Logger logger; MainThreadSupport mainThreadSupport;
配置好EventBusBuilder建造者,那么EventBusBuilder如何使用建造者呢?毕竟EventBus的有参构造方法是私有的,还好EventBusBuilder建造者提供了一个installDefaultEventBus()方法,初始化默认的EventBus单例,注意不是“替换”!如果EventBus已有单例存在,那么调用该方法会报错。
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}或者直接通过EventBusBuilder建造者的build()方法创建一个新的EventBus实例:
/** Builds an EventBus based on the current configuration. */
public EventBus build() {
return new EventBus(this);
}1.4、小结
了EventBus的构造源码,可以知道获取EventBus实例的四种方法:
①EventBus.getDefault() //getDefault()获取默认的全局总线
②new EventBus() //EventBus的两个构造方法只有这个默认的可以直接使用
③EventBus.builder().installDefaultEventBus() //只能设置一次,而且必须先于getDefault()设置
④EventBus.builder().build() //使用EventBusBuilder建造自定义EventBus实例
2、注册订阅者
EventBus事件总线已经拿到了,接下来就该注册订阅者。EventBus库内部通过以下几个映射维护订阅者和事件的关系。
//维护 [事件类型] 到 [订阅者]的映射。某个[事件类型],对应一个或者多个[订阅者]。 private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; //维护[订阅者] 到 [订阅事件类型]的映射。一个[订阅者]可以订阅一个或多个[事件类型]。 private final Map<Object, List<Class<?>>> typesBySubscriber; //记录[事件类型] 到 对应[粘性事件]的映射 。注册粘性订阅者时,会将[粘性事件]再次发送给订阅该[类型事件]的[订阅者]。 private final Map<Class<?>, Object> stickyEvents;
在开始阅读源码之前,最好先要知道这一点,方便后面代码的理解。
2.1、注册订阅者
EventBus的注册流程比解注册要稍微复杂一些,因为涉及到注册对象注解的解析,订阅者、事件类型相互之间的关系映射。整体流程如下:
从register(Object subscriber)方法开始看:
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类,解析订阅对象中的用@Subscribe注解的订阅方法,调用findSubscriberMethods(Class<?> subscriberClass)方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//默认false
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时使用默认的EventBusBuilder,ignoreGeneratedIndex默认配置为false,所以执行findUsingInfo(Class<?> subscriberClass)方法:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//借助FindState来遍历查找注解,这里用到了对象池,从对象池中获取FindState实例
FindState findState = prepareFindState();
//初始化FindState ,findState.clazz初始为null
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();
}
//从FindState中拿出找到的List<SubscriberMethod>,并释放FindState到资源池。
return getMethodsAndRelease(findState);
}值得学习的是,为了提高性能,这里用到了对象池以复用FindState实例。对象池可以模仿小巧的对象池Pools一文的实现。
private static final int POOL_SIZE = 4; private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
在findUsingInfo(Class<?> subscriberClass)方法开始调用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();
}最后再通过getMethodsAndRelease(FindState findState)方法返回订阅对象的注解方法解析结果,并且释放对象回到对象池(如果没满)。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}接着findUsingInfo(Class<?> subscriberClass)方法继续看,内部调用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) {
...
}
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);
//添加解析出的订阅方法到FindState中
} 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");
}
}
} 从上面的方法可以看到几个开发注意事项,这也是EventBus的硬性规定。相信很多开发者都曾经遇到过:
①注解的订阅方法必须有且仅有一个参数,多了或者少了都会抛出异常提示:must have exactly 1 parameter...
②注解的方法必须是public,否则抛出异常提示:@Subscribe method: must be public...
解析完后,回到findSubscriberMethods(Class<?> subscriberClass)方法,会发现还有一处限定:
③注册的对象中没有找到注解的订阅方法,则抛出:have no public methods with the @Subscribe annotation...
绕了一大圈,再回到最初的register(Object subscriber)方法,通过SubscriberMethodFinder找到注册对象中的所有订阅方法。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//执行订阅
subscribe(subscriber, subscriberMethod);
}
}
}现在对它们进行订阅,执行subscribe(Object subscriber, SubscriberMethod subscriberMethod)方法,看着比较多,且一行一行分析:
// Must be called in synchronized block
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);
//下面就是对粘性事件的操作,新注册的对象,如果接受粘性事件,那么将之前发送过的粘性事件再发送给现在注册的粘性订阅者
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>).
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);
}
}
}看到这里,订阅对象已经注册到事件中线中了。本篇暂不对事件的分发展开探究。
2.2、解注册订阅者
解注册一个从来没被注册的对象并不会抛出异常,反而重复注册一个对象会抛出重复注册的异常。通过isRegistered(Object subscriber)方法判断一个对象是否被注册到EventBus中,其实就是判断在不在typesBySubscriber映射中:
public synchronized boolean isRegistered(Object subscriber) {
return typesBySubscriber.containsKey(subscriber);
}订阅者的解注册,就是将订阅对象及对应的订阅事件类型从typesBySubscriber中删除。
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
//需要取消订阅的这个对象,有对应多少个事件,前面注册的时候添加进来的
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
//遍历所有这个订阅者的订阅事件
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//从typesBySubscriber映射中删除
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}再根据事件类型从subscriptionsByEventType拿到这个事件的所有订阅者,并从中删除当前需要取消订阅的对象:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//从subscriptionsByEventType映射中获取所有这种事件类型的订阅者
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 == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}上面的两步对开发者来说一点也不陌生,映射和集合的基本操作。
EventBus源码阅热身完毕,先看到这里。后面还有事件的发送、消息处理等等。
推荐阅读


