這可能是最詳細的 EventBus 源碼分析01 - EventBus 對象的創建
這可能是最詳細的 EventBus 源碼分析02 - EventBus 的註冊(上篇)
這可能是最詳細的 EventBus 源碼分析03 - EventBus 的註冊(下篇)
這可能是最詳細的 EventBus 源碼分析04 - 事件的發送與執行
這可能是最詳細的 EventBus 源碼分析05 - Subscriber Index
上一章分析學習了 EventBus
對象的創建, 本章內容將對 EventBus
的註冊源碼進行分析.
由於代碼較長, 所以分爲上下兩個章節.
- 上篇: 分析的是如何獲取訂閱者類中的訂閱方法的.
- 下篇: 分析的是在獲取到訂閱者類中的所有訂閱方法後如何進行訂閱的流程.
簡單來說本章的核心思想就是: 訂閱者調用 EventBus.getDefault().register(this)
進行註冊. EventBus
就會通過反射尋找訂閱者內所有的訂閱方法, 然後進行挨個遍歷進行訂閱. 如果是粘性 sticky
事件, 在訂閱的時候就取出保存的 sticky
事件直接發送, 這樣就做到了發佈者先發布事件, 之後訂閱者訂閱事件, 接收訂閱之前發佈的粘性事件.
Ps: 上篇只分析是如何獲取訂閱者內所有的訂閱方法.
本章涉及到的類及方法
|---EventBus.register()
| 1---SubscriberMethodFinder.findSubscriberMethods()
| 1.1---SubscriberMethodFinder.findUsingInfo()
| 1.1.1---SubscriberMethodFinder.prepareFindState()
| 1.1.2---FindState.initForSubscriber()
| 1.1.3---SubscriberMethodFinder.getSubscriberInfo()
| 1.1.4---SubscriberMethodFinder.findUsingReflectionInSingleClass()
| 1.1.5---FindState.moveToSuperclass()
| 1.1.6---SubscriberMethodFinder.getMethodsAndRelease()
| 2---EventBus.subscribe()
在下篇進行分析
在 EventBus 3.0
後的版本中增加了註解處理器, 在程序的編譯時候, 就可以根據註解生成相對應的代碼, 相對於之前的直接通過運行時反射, 大大的提高了程序的運行效率. 目前還是先使用最基本的運行時反射的方式來分析, 後面也會根據運行時反射的方式來分析.
EventBus.register()
public void register(Object subscriber) {
//通過反射獲得訂閱者的 Class 對象
Class<?> subscriberClass = subscriber.getClass();
//通過訂閱者 Class 對象找到該對象內所有的訂閱方法集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍歷並進行單個方法的訂閱
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
在 register
方法中, 通過反射獲得我們傳入的 this
的 class
對象, 也就是訂閱者的 class
對象. 然後調用了 subscriberMethodFinder.findSubscriberMethods
傳入訂閱者 class
對象後獲得訂閱對象內的所有訂閱方法的集合. 這個是獲得訂閱方法集合的入口. 接着遍歷所有的訂閱方法挨個對其進行訂閱..
1. SubscriberMethodFinder.findSubscriberMethods()
//是一個方法緩存池. key 爲我們訂閱者的 class 對象. value 爲訂閱者內所有的訂閱方法
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//分析 1
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//分析 2
if (ignoreGeneratedIndex) {
//直接使用反射獲取當前訂閱者類的所有訂閱方法
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//先通過自動生成的自定義索引類 SubscriberIndex 方式獲取,
//獲取不到再通過反射的方式獲取
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//分析 3
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
-
分析 1
從方法緩存池中獲取我們傳入訂閱者的class
對象的所有訂閱方法集合. 若存在就直接返回.
首先看到根據我們傳入的訂閱者的class
對象從METHOD_CACHE
這個map
中獲取一個SubscriberMethod
類型的列表.那麼
METHOD_CACHE
是用來存放什麼呢? 看一下它的聲明.
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
METHOD_CACHE
是一個方法緩存池, 以訂閱者的class
對象爲key
, 以訂閱者內部所有的訂閱方法集合爲value
爲什麼說
METHOD_CACHE
的value SubscriberMethod
是訂閱者內部的所有訂閱方法的集合呢?SubscriberMethod
就是將我們平時使用EventBus
過程中的那些參數基本上都封裝起來了, 例如Method
方法.threadMode
線程模型,eventType
事件類型,priority
優先級,sticky
是否是粘性事件等等. -
分析 2
根具ignoreGeneratedIndex
的值來採用不同的方式獲取訂閱方法的集合ignoreGeneratedIndex
: 是否忽略註解器生成的索引類, 默認爲false
. 可以通過EventBusBuilder
來設置它的值.Ps: 配置生成自定義索引類是
EventBus3.0
後新增的特性. 如何生成索引類以及他的使用,可以參考官方文檔. 我們後面也會分析到. 分析 3
subscriberClass
就是訂閱者的class
對象.
subscriberMethods
就是findUsingInfo(subscriberClass)
返回的當前訂閱者的所有訂閱方法集合.
將當前訂閱者class
對象的所有訂閱方法存儲到METHOD_CACHE
中並返回subscriberMethods
按照默認的 ignoreGeneratedIndex = false
調用findUsingInfo(subscriberClass)
這個方法, 看它是如何拿到當前訂閱者 class
對象內的所有訂閱方法的.
1.1.SubscriberMethodFinder.findUsingInfo()
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
...
}
由於在這個方法內調用的方法較多, 所以會逐個分析.
先是通過調用 prepareFindState()
方法, 獲得了一個 FindState
對象. 接着調用了 FindState.initForSubscriber
方法. 看名字大概能猜得出, 是做了一些初始化的操作.
那我們先來看 prepareFindState()
方法.
1.1.1.SubscriberMethodFinder.prepareFindState()
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
//長度爲 4, 遍歷 findState 對象池
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
//如果遍歷到的對象非空,表示找到了可複用的 findState
if (state != null) {
//將該位置清空, 爲了以後再來複用
FIND_STATE_POOL[i] = null;
//返回這個 findState
return state;
}
}
}
//如果遍歷完都沒有找到可用的 findState, 就創建一個.
return new FindState();
}
首先是初始化了一個長度爲 4 的 FindState
類型數組 FIND_STATE_POOL
, 也可以叫它 FindState
對象池. 這個方法就是從 FindState
對象池中獲取一個 FindState
對象返回, 如果對象池中沒有找到可複用的那麼就創建一個新的對象返回.
從 FIND_STATE_POOL
中取出不爲空的FindState
對象, 然後將對應位置置空, 並返回FindState
對象, 這樣可以避免多線程讀寫衝突以及讀寫不一致情況發生.
在 1.1 中獲取了 FindState
對象後, 又調用了它的 initForSubscriber()
初始化方法. 現在跟進進入 FindState
看一下這是什麼類.
1.1.2.FindState.initForSubscriber
static class FindState {
//用來保存所有的訂閱方法信息
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//以 Event 事件類型爲 key, 訂閱方法爲 value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//key 爲方法名, Value 爲訂閱者 class 對象
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
//subscriberClass 代表的是訂閱者本身,也就是register函數的參數,自始至終不會變
// 會在遍歷方法過程中變化, 比如目前在遍歷父類方法,clazz 變量就指向父類
Class<?> subscriberClass;
Class<?> clazz;
//是否跳過父類. 即對父類也要查找, 默認爲 false
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
//做賦值及清理操作
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
//資源回收
void recycle() {
subscriberMethods.clear();
anyMethodByEventType.clear();
subscriberClassByMethodKey.clear();
methodKeyBuilder.setLength(0);
subscriberClass = null;
clazz = null;
skipSuperClasses = false;
subscriberInfo = null;
}
//判斷訂閱方法是否可以添加到 anyMethodByEventType 中
boolean checkAdd(Method method, Class<?> eventType) {
//通常,訂閱者沒有監聽相同事件類型的方法。但是會有一種情況就是 子類訂閱了當前事件, 它的父類也訂閱了
//如果 key 沒有重複,put 成功,則返回 null, 如果 key 重複了,返回的是被覆蓋前的 valu 值.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
...
anyMethodByEventType.put(eventType, this);
}
//再根據方法簽名進行檢查
return checkAddWithMethodSignature(method, eventType);
}
}
//主要目的還是爲了, 不要出現一個訂閱者有多個相同訂閱方法訂閱同一個事件. 如果有, 就將以前的 value 重新放進去覆蓋掉新的.
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
...
//獲得當前 Method 對象表示的方法的類的 Class 對象
Class<?> methodClass = method.getDeclaringClass();
//這裏也會返回 null, 或者是覆蓋前的 value 值
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
// 爲 null 表示添加成功並且不是覆蓋.
// 父類Class.isAssignableFrom(子類Class) 判斷新添加的類是否是被覆蓋類的子類.
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
return true;
} else {
//如果不滿足上述判斷, 將被覆蓋前的value, 重新放進去, 並返回 false.
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
//將成員變量clazz指向clazz的父類並排除系統類
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
//排除系統類
clazz = null;
}
}
}
}
FindState
類. 它是SubscriberMethodFinder
中的一個靜態內部類. 內部存儲了對訂閱者class
對象一些列的集合, 並保存了一些訂閱者的方法以及對訂閱方法的校驗.
在 FindState
中又出現了一個陌生的 SubscriberInfo
, 這是一個接口, 描述了一個訂閱者應當具備的特性. 這是在使用自動生成的索引類後纔會使用到的. 後面會對配置索引類的流程也進行分析. 這裏先不管這些.
public interface SubscriberInfo {
Class<?> getSubscriberClass();//獲取父類列表
SubscriberMethod[] getSubscriberMethods();//獲取註冊的訂閱方法
SubscriberInfo getSuperSubscriberInfo();//獲取父類(直接父類)的訂閱信息
boolean shouldCheckSuperclass();//是否需要對父類進行檢查
}
繼續回到 1.1 findUsingInfo()
中向下分析
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
FindState findState = prepareFindState();
//傳入訂閱者的 Class 對象, 爲 findState 做一些初始化工作
findState.initForSubscriber(subscriberClass);
//循環直到 findState 的 clazz 爲空( clazz 爲 java. 或 android. 或 javax. 等系統類則結束查找).
while (findState.clazz != null) {
//如果沒有使用索引類的情況, 這裏默認基本都是返回 null
findState.subscriberInfo = getSubscriberInfo(findState);
...
}
...
}
看到初始化完 FindState
後, 調用了 getSubscriberInfo()
方法將返回值賦值給 findState.subscriberInfo
. 不過在沒有使用索引類的情況下, 這裏返回的基本都是 null
. 跟進 getSubscriberInfo()
方法.
1.1.3.SubscriberMethodFinder.getSubscriberInfo(findState)
private SubscriberInfo getSubscriberInfo(FindState findState) {
//分析 1
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//分析 2
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
分析 1
在FindState.initForSubscriber()
的方法中, 就已經將subscriberInfo
置爲了null
. 所以第一個if
不會進入.分析 2
在沒有使用索引類的情況下,subscriberInfoIndexes
是在SubscriberMethodFinder
的構造方法傳入的, 默認爲null
, 所以這裏直接返回了null
.
如果配置了索引類, 就會進入到分析 2 的if
內.
這裏只考慮沒有使用索引類的情況. 所以直接返回 null
, 繼續回到 1.1findUsingInfo ()
中
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
FindState findState = prepareFindState();
//傳入訂閱者的 Class 對象, 爲 findState 做一些初始化工作
findState.initForSubscriber(subscriberClass);
//循環直到 findState 的 clazz 爲空( clazz 爲 java. 或 android. 或 javax. 等系統類則結束查找).
while (findState.clazz != null) {
//如果沒有使用索引類的情況, 這裏默認基本都是返回 null
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
...
} else {
//在運行時利用反射來獲取所有被 @Subscribe 註解標註的訂閱方法
findUsingReflectionInSingleClass(findState);
}
}
...
}
接着就進入到 else
中的 findUsingReflectionInSingleClass
方法, 開始利用反射來獲取所有添加了@Subscribe
註解的的訂閱方法.接着跟進去.
1.1.4.SubscriberMethodFinder.findUsingReflectionInSingleClass(findState)
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//通過反射獲取訂閱者 class 對象內的所有方法.
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
...
...
}
//對獲取到的所有方法進行遍歷
for (Method method : methods) {
//獲取到方法的修飾符
int modifiers = method.getModifiers();
//判斷修飾符是否是 public, 並且是否可以忽略
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲取方法的參數, 後面進行參數的判斷, EventBus 只允許訂閱事件中才參數只有一個
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//判斷這個方法是否有 @Subscribe 註解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//獲得 Event 事件的 Class 對象.
Class<?> eventType = parameterTypes[0];
//檢查是否需要保存到 findState 的 anyMethodByEventType 中, 返回 true 表示添加成功
if (findState.checkAdd(method, eventType)) {
//獲得這個方法註解的 ThreadMode 線程模式, 根據不同的模式進行不同的線程調度
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 往 findState 用來保存所有的訂閱方法信息集合裏面添加 SubscriberMethod (方法以及註解信息的封裝類) ,
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//拋異常
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//拋異常
}
}
}
這段代碼的執行過程其實並不是很複雜. 在這裏主要是使用了Java
的反射和對註解的解析.
- 首先通過反射來獲取訂閱者中所有的方法
- 遍歷所有方法.
- 根據方法的類型, 參數和註解來找到真正的訂閱方法.
- 找到訂閱方法後調用
findState.checkAdd(method,eventType)
判斷是否能將訂閱方法相關信息保存到FindState.subscriberMethods
當中.
注: (findState.checkAdd
在上面 1.1.2 FindState
中有詳細描述)
到這裏已經完成對訂閱者中所有訂閱方法的查找. 接着再回到 1.1findUsingInfo()
中, 看到findUsingInfo()
的while
循環中還調用了一個方法, 就是FindState.moveToSuperclass()
.
1.1.5 FindState.moveToSuperclass()
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
FindState findState = prepareFindState();
//傳入訂閱者的 Class 對象, 爲 findState 做一些初始化工作
findState.initForSubscriber(subscriberClass);
//循環直到 findState 的 clazz 爲空( clazz 爲 java. 或 android. 或 javax. 等系統類則結束查找).
while (findState.clazz != null) {
//如果沒有使用索引類的情況, 這裏默認基本都是返回 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 中保存所有訂閱方法信息的集合中
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//在運行時利用反射來獲取所有被 @Subscribe 註解標註的訂閱方法
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
//資源回收,並返回用來保存所有的訂閱方法信息的集合(當前訂閱者 class 對象中所有的訂閱方法集合)
return getMethodsAndRelease(findState);
}
//將成員變量clazz指向clazz的父類並排除系統類
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
//排除系統類
clazz = null;
}
}
}
這裏的邏輯就是 在第一次循環的時候, FindState.clazz
指向的就是當前訂閱者類, 第一次循環結束的時候, 調用 moveToSuperclass()
如果當前訂閱者有非系統類的父類, 就將 FindState.clazz
指向父類, 開始第二次循環, 接着查找父類中所有的訂閱方法, 依次類推.
獲取最後又調用了getMethodsAndRelease(findState)
方法並返回. 見名知意, 獲取所有的方法並釋放.
1.1.6 SubscriberMethodFinder. getMethodsAndRelease(FindState)
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
//先獲取 findState 中用來保存所有的訂閱方法信息的集合
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
//回收重置 findState.
findState.recycle();
//重置 findState 對象池
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;
}
getMethodsAndRelease
就先獲取FindState.subscriberMethods
, 接着將FindState
中的內容及數據重置, 最後重置FindState
對象池.以便複用. 最後返回 subscriberMethods
.
至此, EventBus.getDefault().register(this)
註冊方法的第一步獲取訂閱者內所有的訂閱方法就分析完了, 下一章將會遍歷這些方法, 依次對其進行訂閱.