订阅者索引
默认情况下,EventBus使用Java反射来查找订阅者信息。订阅者索引是EventBus 3的一个新特性。它可以加速查找订阅者信息的过程,是一个可选的优化。订阅者索引的原理是:使用EventBus的注解处理器在应用构建期间创建订阅者索引类,该类已经包含了所有的订阅者信息。EventBus官方推荐在Android中使用订阅者索引以获得最佳的性能。
要开启订阅者索引的生成,你需要在构建脚本中使用annotationProcessor属性将EventBus的注解处理器添加到应用的构建中,还要设置一个eventBusIndex参数来指定要生成的订阅者索引的完全限定类名。
首先,修改模块下的build.gradle构建脚本。如下所示:
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'com.github.cyc.eventbus.subscriberindexdemo.MyEventBusIndex']
}
}
}
...
}
dependencies {
...
compile 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
然后,build一下工程。EventBus注解处理器将为你生成一个订阅者索引类。示例代码如下所示:
package com.github.cyc.eventbus.subscriberindexdemo;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.Map;
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onMessageEvent", MessageEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
可以看到,所有的订阅者信息都先被保存在SUBSCRIBER_INDEX静态成员变量中。后面查找订阅者信息时,EventBus就可以直接调用getSubscriberInfo()方法来获取该订阅者的信息了。
最后,在应用自定义的Application类的onCreate()方法中将订阅者索引添加到EventBus中,并将该EventBus设置成默认的EventBus。示例代码如下所示:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 配置EventBus
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
}
}
我们来看builder()、addIndex()和installDefaultEventBus()方法的代码:
public class EventBus {
static volatile EventBus defaultInstance;
...
public static EventBusBuilder builder() {
return new EventBusBuilder();
}
}
public class EventBusBuilder {
...
List<SubscriberInfoIndex> subscriberInfoIndexes;
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
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;
}
}
public EventBus build() {
return new EventBus(this);
}
}
builder()静态方法新建了一个EventBusBuilder对象。addIndex()方法将生成的订阅者索引添加到EventBusBuilder对象的subscriberInfoIndexes成员变量中。installDefaultEventBus()方法调用了build()方法新建了一个EventBus对象,并将该对象赋值给EventBus的defaultInstance静态成员变量。这样,后面调用EventBus.getDefault()静态方法获取到的就是该EventBus对象了。我们接着来看EventBus.getDefault()静态方法的代码:
public class EventBus {
static volatile EventBus defaultInstance;
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
private final SubscriberMethodFinder subscriberMethodFinder;
...
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
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;
}
}
getDefault()静态方法使用了单例模式,保证只有一个默认的EventBus实例。EventBus实例的创建使用了建造者模式。无论是调用了installDefaultEventBus()方法,还是只调用了getDefault()静态方法,最终都调用了EventBus(EventBusBuilder builder)构造方法来创建EventBus实例。EventBus()构造方法主要是初始化了一些数据结构和复制了EventBusBuilder对象的配置值:
- subscriptionsByEventType成员变量以事件类型的Class对象为键,用于保存订阅了该事件类型的所有的订阅者信息。
- typesBySubscriber成员变量以订阅者对象为键,用于保存该订阅者订阅的所有的事件类型。
- stickyEvents成员变量以事件类型的Class对象为键,用于保存该事件类型最近一次发布的粘性事件。
- mainThreadPoster成员变量用于ThreadMode.MAIN和ThreadMode.MAIN_ORDERED线程模式下事件的传递。
- backgroundPoster成员变量用于ThreadMode.BACKGROUND线程模式下事件的传递。
- asyncPoster成员变量用于ThreadMode.ASYNC线程模式下事件的传递。
- subscriberMethodFinder成员变量用于查找订阅者方法。它保存了EventBusBuilder对象的subscriberInfoIndexes成员变量,也就是订阅者索引。
订阅事件
订阅事件主要分为两个过程:
- 注册订阅者
- 注销订阅者
调用register()方法可以注册一个订阅者。我们先来看register()方法的代码:
public class EventBus {
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
...
}
register()方法先调用了subscriberMethodFinder成员变量的findSubscriberMethods()方法来查找该订阅者的订阅者方法,然后才调用subscribe()方法开始注册订阅者。所以,我们先来分析一下查找订阅者方法的过程。
查找订阅者方法
我们来看findSubscriberMethods()方法的代码:
class SubscriberMethodFinder {
...
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
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;
}
}
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
}
findSubscriberMethods()方法先从METHOD_CACHE缓存中获取订阅者方法。如果获取到了订阅者方法,那么直接返回。反之,开始查找订阅者的订阅者方法。ignoreGeneratedIndex成员变量表示是否忽略生成的订阅者索引,它来自EventBusBuilder对象的ignoreGeneratedIndex成员变量,默认值为false,表示不忽略。因此,默认情况下,将调用findUsingInfo()方法来查找订阅者方法。如果用户没有生成或者添加订阅者索引的话,那么findUsingInfo()方法将与findUsingReflection()方法一样最终调用findUsingReflectionInSingleClass()方法使用Java反射来查找订阅者方法。如果没有查找到订阅者方法,那么将抛出EventBusException异常。反之,先更新METHOD_CACHE缓存,然后再返回。我们接着来看findUsingInfo()方法的代码:
class SubscriberMethodFinder {
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);
}
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
...
}
findUsingInfo()方法使用了while循环,不仅查找了订阅者的订阅者方法,而且查找了订阅者父类的订阅者方法。findUsingInfo()方法先调用了getSubscriberInfo()方法来获取订阅者信息。如果生成并添加了订阅者索引,即subscriberInfoIndexes成员变量不为null,那么getSubscriberInfo()方法将遍历subscriberInfoIndexes成员变量来查找订阅者信息。如果获取到了订阅者信息,那么将订阅者信息里的订阅者方法保存起来。反之,调用findUsingReflectionInSingleClass()方法来查找订阅者方法。我们接着来看findUsingReflectionInSingleClass()方法的代码:
class SubscriberMethodFinder {
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
methods = findState.clazz.getMethods();
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");
}
}
}
...
}
findUsingReflectionInSingleClass()方法使用了Java反射来查找订阅者方法。只有那些同时满足public访问权限、非static方法、非abstract方法、有且只有一个参数和使用了@Subscribe注解的方法才被当做是合法的订阅者方法。
注册订阅者
register()方法在查找到订阅者所有的订阅者方法之后,遍历订阅者方法为每个订阅者方法调用了subscribe()方法。我们接着来看subscribe()方法的代码:
public class EventBus {
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);
}
}
}
...
}
首先,subscribe()方法创建了一个包含了该订阅者方法和对应订阅者的Subscription对象,并根据订阅者方法的事件传递优先级将该Subscription对象添加到对应事件类型的Subscription列表中。然后,将该订阅者方法对应的事件类型添加到对应订阅者的订阅事件类型列表中。最后,如果是订阅了粘性事件的订阅者方法,那么会先获取对应事件类型的粘性事件并最终调用postToSubscription()方法将该粘性事件发送给订阅者。
注销订阅者
调用unregister()方法可以注销一个订阅者。我们接着来看unregister()方法的代码:
public class EventBus {
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
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--;
}
}
}
}
...
}
首先,unregister()方法获取了该订阅者订阅的所有的事件类型列表。然后,遍历事件类型列表对每个事件类型调用unsubscribeByEventType()方法。unsubscribeByEventType()方法先获取订阅了该事件类型的所有的Subscription列表,然后从列表中删除了与该订阅者相关的Subscription对象。最后,从typesBySubscriber成员变量中删除了该订阅者订阅的所有的事件类型列表。
发布事件
调用post()方法可以发布一个事件。我们接着来看post()方法的代码:
public class EventBus {
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 {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
...
}
post()方法先将事件添加到当前线程的PostingThreadState对象的事件队列的末尾。最终调用了postSingleEvent()方法将事件发布出去。我们接着来看postSingleEvent()方法的代码:
public class EventBus {
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++) {
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));
}
}
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
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);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
...
}
eventInheritance成员变量表示是否需要考虑事件的继承,即是否要发布对应事件类型的父类型的事件。它来自EventBusBuilder对象的eventInheritance成员变量,默认值为true,表示需要考虑事件的继承。因此,默认情况下,postSingleEvent()方法不仅调用了postSingleEventForEventType()方法发布了对应事件类型的事件,而且发布了对应事件类型的父类型的事件。postSingleEventForEventType()方法先获取订阅了该事件类型的所有的Subscription列表。然后遍历Subscription列表对每个Subscription对象调用了postToSubscription()方法将事件发布出去。我们接着来看postToSubscription()方法的代码:
public class EventBus {
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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
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);
}
}
...
}
postToSubscription()方法针对每一种线程模式使用了对应的发布事件策略:
- ThreadMode.POSTING。在当前发布事件的线程中直接调用订阅者方法。
- ThreadMode.MAIN。如果当前线程就是主线程,那么直接调用订阅者方法。反之,先进入队列,后面将在主线程的Handler中调用订阅者方法。
- ThreadMode.MAIN_ORDERED。直接进入队列,后面将在主线程的Handler中调用订阅者方法。
- ThreadMode.BACKGROUND。如果当前线程不是主线程,那么直接调用订阅者方法。反之,先进入队列,后面将在线程池的工作线程中调用订阅者方法。
- ThreadMode.ASYNC。直接进入队列,后面将在线程池的工作线程中调用订阅者方法。
以上调用订阅者方法都是通过调用invokeSubscriber()方法以Java反射的方式调用的。