EventBus使用很簡單,了解它的源碼的實(shí)現(xiàn)也是應(yīng)該的,不但學(xué)習(xí)設(shè)計(jì)者的思想,也可以提高自己的能力。
注冊
EventBus.getDefault().register(this);
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可見,EventBus使用注解和反射機(jī)制。查看findSubscriberMethods,查找當(dāng)前注冊類是否有訂閱方法。
整個查找訂閱方法就過程開始了
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;
}
}
- 通過
METHOD_CACHE獲取是否有緩存該類的所有方法,有緩存則直接返回一個List<SubscriberMethod>。METHOD_CACHE是一個ConcurrentHashMap,以訂閱類的Class為key,保存所有的訂閱方法List<SubscriberMethod>為value。 - 通過跟蹤代碼,
ignoreGeneratedIndex默認(rèn)值為false,所有這里執(zhí)行findUsingInfo方法。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//finaState保留訂閱類的相關(guān)信息
FindState findState = prepareFindState();
//findState的初始化
findState.initForSubscriber(subscriberClass);
//由于上一行的初始化,clazz!=null
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);
}
配合代碼中的注釋,F(xiàn)indState類主要保留注冊類的相關(guān)信息,在prepareFindState和initForSubscriber,初始化后,findSate.clazz并不為空,findSate指向當(dāng)前的訂閱類的class。查看getSubscriberInfo
,此時返回值是null。在前面初始化中,findState.subscriberInfo的值是null,為此查看findUsingReflectionInSingleClass。
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");
}
}
}
- 這里通過反射機(jī)制查看訂閱類是否有訂閱方法。
- 符合訂閱方法規(guī)則。
- 訂閱方法必須public修飾,不能是靜態(tài)方法和抽象方法。否則會拋出錯誤。
- 參數(shù)只有一個的。
- 使用Subscribe注解的
- 在符合第二個要求后,通過
checkAdd方法檢測訂閱方法是否已存在。如果不存在則封裝成SubscriberMethod保存在findState.subscriberMethods。
在這里,我們查看一下FindState類的初始化和checkAdd方法。
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
//該類型參數(shù)未添加
return true;
} else {
if (existing instanceof Method) {
//存在相同類型參數(shù),可能方法名不同,或者父類被子類重寫了。
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
//檢測方法名是否相同
//檢測methodClassOld是否是methodClass的父類或者接口
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
-
subscriberMethods用于保存所有訂閱方法。anyMethodByEventType以事件類型EventType為key,保存方法Method。subscriberClassByMethodKey以method.getName()>eventType.getName()為key,保存methodClass。 -
checkAdd的第一步是檢查該事件類型的參數(shù)的方法是否已經(jīng)存在,如果不存在,直接返回true添加,添加該訂閱方法。第二步是,但存在該類型方法時,可能存在方法名不同或者方法名相同父類被子類重寫。此時調(diào)用checkAddWithMethodSignature方法進(jìn)行檢查。 - 在
checkAddWithMethodSignature方法中,當(dāng)methodClassOld==null,表示方法名不同,那么直接添加訂閱方法。methodClassOld.isAssignableFrom(methodClass)表示methodClassOld是否methodClass的父類或者父接口。如果是的化,也直接添加。
這里的雙重檢查,判斷該訂閱方法是否已存在:
- 與已有的訂閱方法參數(shù)類型是否相同,不相同,則直接添加。相同則下一步。
- 檢測方法名是否相同,不相同則添加。相同則檢查已有的訂閱方法的類是否是訂閱類的父類或父接口。
主要是查找所有的訂閱方法,并封裝為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;
}
}
當(dāng)注冊類后但沒有訂閱方法后,會報異常
把查到到的訂閱方法放到METHOD_CACHE,以便下次查找訂閱方法直接返回。
到這里整個查找訂閱方法結(jié)束了
接著回到最開始的方法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);
}
}
}
可見,通過subscribe方法將訂閱者和其所有的訂閱方法關(guān)聯(lián)在一起。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//Subscription保存著訂閱者和訂閱方法的信息和狀態(tài)
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//獲取該事件類型的所有訂閱者和訂閱方法
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
// 如果事件類型為首次添加
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果事件類型非首次添加,并存在當(dāng)前訂閱信息,表示該訂閱者已經(jīng)添加該類型的訂閱方法,拋出異常。
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//根據(jù)事件的優(yōu)先權(quán),順序添加到subscriptions中
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);
}
}
}
- 主要將訂閱者和訂閱方法關(guān)聯(lián)起來,封裝成
Subscription,并以事件類型為key保存到subscriptionsByEventType中。 - 如果
subscriptionsByEventType已有訂閱信息,會拋出異常。 - 如果訂閱方法是粘性的,則會收到該類型的最后一條粘性廣播。
到這里,EventBus的注冊過程基本就結(jié)束了。
整個流程總結(jié):
1、 尋訂閱類是否有訂閱方法,是否符合要求
2、 將訂閱方法相關(guān)信息封裝成SubscriberMethod,并把所有訂閱方法存在List和緩存到METHOD_CACHE中。
3 、將訂閱者和訂閱方法關(guān)聯(lián)起來,封裝成Subscription。并保存在subscriptionsByEventType。
4、 如果是粘性事件,接收最后一條廣播。
通過注冊流程中,我們應(yīng)該也能猜想EventBus的工作原理了。當(dāng)我們post一個eventType時,去讀取subscriptionsByEventType中該類型的相關(guān)訂閱信息,然后達(dá)到調(diào)用我們訂閱方法。也就是說明為什么EventBus是屬于發(fā)布-訂閱模式。
分析下來,涉及到了單例模式,建造者模式,觀察者模式。