EventBus3.0源碼解析-01這篇文章簡單的介紹了EventBus的使用流程,我們知道EventBus有三個主要入口方法,分別爲
-
EventBus.getDefault().register(Object);
-
EventBus.getDefault().unregister(Object);
-
EventBus.getDefault().post(Object);
本文將通過register入口方法及其邏輯,分析EventBus源碼。
EventBus的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);
}
}
}
register函數主要實現了兩個功能,1、將訂閱類中所有訂閱方法收集;2、按一定規則保存收集的信息。
訂閱方法收集
訂閱方法收集主要由SubscriberMethodFinder類的findSubscriberMethods函數實現,我們看下其具體實現:
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;
}
}
1、如果參數中的subscriberClass類之前註冊過,那麼直接從METHOD_CACHE緩存中獲取信息返回。
2、根據ignoreGeneratedIndex判斷是否要根據註解處理器在編譯期生成的索引文件來尋找訂閱方法信息,該方式是從EventBus3.0中引入的,目的是爲了提高EventBus的整體效率。如果ignoreGeneratedIndex爲true,則使用反射來尋找訂閱方法信息,否則,先根據生成的索引文件來尋找訂閱方法信息,索引文件中找不到,再使用反射尋找。
3、如果找到的訂閱方法信息非空,則將這些信息緩存在METHOD_CACHE中。
第1、3步沒有什麼好講的內容,主要的內容在第2步。由於根據索引文件尋找訂閱方法信息的方式還涉及註解處理器,稍微複雜,所以會單獨開一篇文章講這塊。本文主要介紹通過反射來尋找訂閱方法信息的方式。
我們來看一下findUsingReflection的具體實現:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 遍歷訂閱類以及其父類
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
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();
}
prepareFindState()方法會嘗試從緩存中尋找是否有現存的FindState對象,如果有,則直接使用緩存的FindState對象,否則,直接new一個FindState對象。
void initForSubscriber(Class<?> subscriberClass) {
// 保存訂閱類信息
this.subscriberClass = clazz = subscriberClass;
// 是否忽略父類
skipSuperClasses = false;
subscriberInfo = null;
}
initForSubscriber方法就是簡單的初始化了一些參數。
接下來我們看下核心方法findUsingReflectionInSingleClass的實現:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// getDeclaredMethods()方法的效率要優於getMethods()
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
// 獲取方法的修飾符, 比如public, static等修飾符
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 獲取方法的參數信息
Class<?>[] parameterTypes = method.getParameterTypes();
// 方法只有一個參數
if (parameterTypes.length == 1) {
// 獲取方法的@Subscribe註解信息
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果方法有@Subscribe註解信息
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
// 檢查當前method是否是有效的訂閱方法
if (findState.checkAdd(method, eventType)) {
// 如果當前method是有效的訂閱方法,則把該訂閱方法收集起來
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方法加了詳細的註解,大多數邏輯都能直接看代碼註釋弄明白。這裏主要要介紹的是findState.checkAdd方法,checkAdd的實現如下:
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) {
return true;
} else {
if (existing instanceof Method) {
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);
}
}
考慮到同一個類裏面通常只會有一個方法監聽同一類事件,爲了效率,所以EventBus先根據事件類型去匹配同一個類中是否已經有添加過的方法監聽同一類事件,如果沒有,則直接認爲該方法爲有效訂閱方法,如果之前已經添加過方法監聽同一類事件,則通過checkAddWithMethodSignature來判斷該方法是否爲有效訂閱方法。checkAddWithMethodSignature的實現如下:
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;
}
}
根據一定規則計算出方法的唯一特徵名稱,如果具有該特徵名稱的方法之前還沒有添加過,則認爲當前方法爲有效訂閱方法。否則,如果之前添加過的方法所在的類是當前方法所在類的父類或者接口,考慮到子類重寫,認爲當前方法爲有效訂閱方法。否則認爲當前方法爲非有效訂閱方法。
通過上面的多次調用findUsingReflectionInSingleClass方法,我們已經把有效的訂閱信息收集到了findState的subscriberMethods變量中。
保存所有訂閱信息
我們再回到findUsingReflection方法,接着會調用getMethodsAndRelease(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;
}
它其實做的事情就是把findState放入到緩存中,然後返回剛剛收集到的有效訂閱信息。
現在我們回到register方法,我們收集到了有效的訂閱信息,接着會循環調用subscribe(subscriber, 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);
}
}
}
這個函數的代碼雖然很長,但是其只做了三件事情,1、將訂閱信息以事件類型爲key保存在subscriptionsByEventType變量中(方便post方法調用的時候,以事件爲key尋找訂閱信息);2、將事件類型以訂閱類爲key保存在typesBySubscriber變量中(方便unregister方法調用的時候,以訂閱類爲key尋找事件類型,然後根據subscriptionsByEventType變量找到訂閱信息);3、處理sticky事件邏輯。關於第3點,考慮到本文篇幅,我將另外開一篇文章進行分析。
總結
所以通過調用register方法,最終訂閱方法信息會被最終保存在subscriptionsByEventType變量和typesBySubscriber變量中。
如果你喜歡,感謝給個贊。如果有什麼問題,歡迎評論。