EventBus 源碼隨想
首先網(wǎng)上已經(jīng)有不少優(yōu)秀的EventBus的源碼分析文章啼肩,這篇只是為了記錄自己的理解祈坠,畢竟自己親自寫出來才能理解的更深赦拘,所以如有不對的地方躺同,還望諒解丸逸。
參考
http://www.reibang.com/p/f057c460c77e
http://p.codekk.com/blogs/detail/54cfab086c4761e5001b2538
https://kymjs.com/code/2015/12/16/01/
0. 幾個問題
EventBus 的使用過程無非就是 注冊车海、post侍芝、響應(yīng)事件函數(shù)埋同。
那么我們得弄清楚這幾個問題
1咧栗、怎樣進(jìn)行注冊的 致板?
2咏窿、post 的事件是 什么集嵌,即 post 里的參數(shù)到底代表著什么怜珍?
3凤粗、post 時嫌拣,訂閱者是怎樣收到響應(yīng)的亭罪,是怎么通知到所有與事件相關(guān)的訂閱者的?
1. 注冊
// 把當(dāng)前類注冊為訂閱者(Subscriber)
EventBus.getDefault().register(this);
// 解除當(dāng)前類的注冊
EventBus.getDefault().unregister(this);
代碼很簡單情组,就一行院崇,那么我們來看看到底是怎樣注冊的底瓣。在注冊的時候究竟做了什么捐凭。在整個 EventBus 的使用過程中茁肠,除了注冊大部分就是 post 和 事件響應(yīng)函數(shù)了垦梆,所以我們猜測在注冊的時候托猩,應(yīng)該會把事件和訂閱者綁定起來京腥。那么我們帶著這個疑惑去看這段代碼绞旅。
1.1 獲取 EventBus 對象
首先 Event.getDefault() 看到這個應(yīng)該能想到是個 單例模式因悲。
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
果不其然讯检,一個雙重校驗鎖的單例模式人灼。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
// 省略
}
可以看到上面構(gòu)造函數(shù)傳入了一個 builder投放,很明顯 建造者模式。 至此拜姿,我們可以獲取到了一個單例的 EventBus 的對象了蕊肥,另外關(guān)于 builder 的更詳細(xì)的內(nèi)容可自己看源碼。
1.2 register
獲取到對象后裸准,就調(diào)用 register 方法了狼速,里面?zhèn)魅肓?this 參數(shù)。
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 方法的參數(shù)名是 subscriber惊完,說明我們傳進(jìn)去的 this 就是訂閱者對象处硬。此時我們清楚了 當(dāng)前類就是訂閱者荷辕。
這個方法里面做了什么呢疮方,我們來看一看骡显。首先獲取到傳進(jìn)去的 this (即當(dāng)前類)的 Class 對象疆栏,然后調(diào)用 subscriberMethodFinder.findSubscriberMethods(subscriberClass); 我們根據(jù)名字可以猜想,這應(yīng)該是去找到當(dāng)前訂閱者的所有 事件響應(yīng)函數(shù)(即帶有 @Subscribe 注解的方法)惫谤。 找到所有事件響應(yīng)函數(shù)后壁顶,就調(diào)用 subscribe, 那這里我們就可以猜想是把 當(dāng)前類對象 與這些 事件響應(yīng)函數(shù) 關(guān)聯(lián)起來溜歪。下面驗證我們的猜想若专。
1.2.1 findSubscriberMethods
首先我們得找到當(dāng)前類的所有 合法的事件響應(yīng)函數(shù)
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;
}
}
這里面我們直接找到 findUsingReflection 方法, 根據(jù)方法名也知道這是什么意思了蝴猪。 繼續(xù)跟進(jìn)
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
這里的 FindState 用于做 事件響應(yīng)函數(shù) 的校驗和保存。繼續(xù)跟進(jìn) 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");
}
}
}
這里如果知道反射的應(yīng)該都看得懂,沒什么復(fù)雜的地方爬早。 從這里的實(shí)現(xiàn)我們可以知道饶米,這個 eventType 其實(shí)是第一個參數(shù)類型, 并且保證只能有一個參數(shù)。 這樣我們的事件其實(shí)是以 事件響應(yīng)函數(shù)的 參數(shù)類型 為基準(zhǔn)的,可以看到如果參數(shù)的數(shù)量不為 1 软吐,會拋出異常使兔。
最后再通過 findUsingReflection 方法的 getMethodsAndRelease 返回一個 List<SubscriberMethod>
至此泽艘, register 方法的 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
這一行代碼就走完了然低,我們拿到了該訂閱類的所有 事件響應(yīng)函數(shù) 枫笛, 即 SubscriberMethod 的集合无畔。
1.2.2 subscribe
接著繼續(xù) register 往下走,可以看到會循環(huán) List<SubscriberMethod>
這個集合薄风,取出每個 SubscriberMethod(事件響應(yīng)函數(shù))撇他,然后調(diào)用 subscribe,我們看看這個方法到底做了什么。
這里先給出結(jié)果,這個方法就是我們注冊的關(guān)鍵了,它完成了我們每個 事件響應(yīng)函數(shù)的注冊過程榛臼。
首先這個方法會涉及兩個變量路呜,我一開始特別懵抵屿,完全不知道這兩個變量啥意思,不過圖一畫出來就立馬清晰了尿扯。
這兩個變量是 subscriptionsByEventType 和 typesBySubscriber 矩屁”僮冢基本上理解這兩個變量是什么意思出牧,整個注冊流程就通了。
首先我們定義兩個類和方法士袄,如下所示,這個得認(rèn)真理清楚这敬。
public class A {
@Subscribe
public void testA1(Event1 event1) {
//do something
}
@Subscribe
public void testA2(Event2 event2) {
//do something
}
}
public class B {
@Subscribe
public void testB1(Event1 event1) {
//do something
}
@Subscribe
public void testB2(Event2 event2) {
//do something
}
}
可以看到這兩個類分別有著兩個事件響應(yīng)函數(shù),事件類型有兩種孕锄,Event1 和 Event2
subscriptionsByEventType 這個變量是 Map<Class<?>, CopyOnWriteArrayList<Subscription>>
類型抡砂,它的圖示如下:
typesBySubscriber 這個變量是 Map<Object, List<Class<?>>>
類都,它的圖示如下:
至于其中的 SubscriberMethod 對象绞呈,在 findUsingReflectionInSingleClass 方法中可以看到是怎樣構(gòu)造出來的志鹃。這個對象也是非常重要的秘血,里面封裝了 方法信息 method、線程模式 threadMode评甜、參數(shù)類型 eventType、優(yōu)先級 priority韧骗、sticky 布爾值 等信息蚂会。
相信這兩個圖可以幫助你很好的理解接下來的 subscribe 流程酵镜。關(guān)于 subscribe 的源碼靠粪,以及流程我就不分析了,自己看源碼對著上面兩個圖絕對能理解毫蚓。 不能理解的話庇配,還有 參考 內(nèi)的文章,這里一定得自己去弄懂绍些。當(dāng)然并不復(fù)雜,所以耐心點(diǎn)耀鸦。
當(dāng) subscribe 走完后柬批,我們的 EventBus 擁有了什么?
它擁有了 事件類型(例 Event1) 所對應(yīng)的 Subscription 信息列表袖订,什么意思氮帐,就是這個 事件 在 哪些類的哪些方法存在,有了這些信息洛姑,EventBus 就能很方便的將一個 事件 傳遞給所有 訂閱了這個事件的 方法了上沐。
那么 typesBySubscriber 有什么用呢,在 unregister 時楞艾,它可以發(fā)揮它的作用参咙。那么趁熱打鐵龄广,看看 unregister 是如何工作的。噢蕴侧,對了择同,放上注冊的流程圖,是取自 codekk 的流程圖净宵,相信再看這個圖敲才,你會非常清晰。
2 unregister 解除注冊
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());
}
}
第一行就用到了剛剛的 typesBySubscriber择葡, 這里將當(dāng)前訂閱者的 事件類型列表 取出來紧武,也就是拿到了當(dāng)前類的所有 事件。
然后判斷 事件列表 是否為空敏储,不為空則 循環(huán)事件列表阻星,依次調(diào)用 unsubscribeByEventType(subscriber, eventType);
最后調(diào)用 typesBySubscriber.remove(subscriber);
這個是把 當(dāng)前訂閱者 從 typesBySubscriber 中刪掉,這樣就完成了解除綁定的操作虹曙。當(dāng)然最重要的是 unsubscribeByEventType 這個方法迫横。
2.1 unsubscribeByEventType
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--;
}
}
}
}
這個根據(jù)上面的 subscriptionsByEventType 圖示,應(yīng)該不難理解酝碳。
首先從 subscriptionsByEventType 中拿到當(dāng)前 事件(eventType)的 Subscription 列表矾踱。 然后循環(huán)這個列表,從中找到傳進(jìn)來的 訂閱者(subscriber 即 當(dāng)前解除注冊的那個 類疏哗,找到后呛讲,從 Subscription 列表 中刪除即可。
3. post 發(fā)布事件
前面注冊和解除注冊兩個流程都走通了返奉,那么只剩下 post 了贝搁。關(guān)于 post 的流程,參考內(nèi)的文章都講的很好芽偏,礙于篇幅雷逆,就不重復(fù)闡述了,這里說說我剛看時污尉,不懂的幾個地方膀哲。
1、 postSingleEvent 方法會先去得到該事件類型的所有父類及接口類型被碗,然后循環(huán) 調(diào)用 postSingleEventForEventType 函數(shù)發(fā)布每個事件到每個訂閱者某宪? 這里說了這么多是什么意思。
2锐朴、 ThreadMode 對應(yīng)的四種狀態(tài)的 Poster兴喂。
針對第一個問題很好解釋,我們基于上面的 Event1 事件解釋,假設(shè)有一個 訂閱者訂閱了 Object 類型的事件衣迷,而 Event1 的父類是 Object畏鼓,那么我們 post Event1事件 時, 訂閱了 Event1 事件的訂閱者 和 訂閱了 Object 事件的訂閱者 是不是都得接收到這個事件蘑险。所以我們需要處理父類及其接口類型的 post滴肿。
第二個問題,看了 post 的源碼應(yīng)該知道最后會進(jìn)入到 postToSubscription 這個方法佃迄,判斷 subscription.subscriberMethod.threadMode 然后調(diào)用 invokeSubscriber 完成 post流程泼差。
那么這個 threadMode 有四種狀態(tài),
3.1 POSTING
首先看下這個 POSTING 狀態(tài)呵俏,這是 默認(rèn)的 ThreadMode堆缘,表示在執(zhí)行 Post 操作的線程直接調(diào)用訂閱者的事件響應(yīng)方法,不論該線程是否為主線程(UI 線程)普碎,也就是說你是哪個線程就會在哪個線程執(zhí)行吼肥。 代碼中可以看到直接調(diào)用了 invokeSubscriber ,看看 invokeSubscriber 方法的源碼麻车。
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);
}
}
就是通過反射執(zhí)行了 method 缀皱, 這樣我們也就清楚了,事件函數(shù)得到了響應(yīng)动猬。
注意: 若 Post 線程為主線程啤斗,別忘了不能進(jìn)行耗時操作
3.2 MAIN
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
這個狀態(tài)我們可以看到,首先會判斷是否是 主線程赁咙, 如果是直接調(diào)用 invokeSubscriber 钮莲,如果不是會去調(diào)用 mainThreadPoster.enqueue 。 那么這個 mainThreadPoster 是什么彼水?
3.2.1 mainThreadPoster
這個 mainThreadPoster 會在構(gòu)造函數(shù)中進(jìn)行初始化崔拥,自己可以去源碼里看看, 可以知道最后就是 new 一個 HandlerPoster凤覆。
看看 HandlerPoster 的構(gòu)造函數(shù)
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
這個 looper 是主線程的 looper链瓦,也就是說最后發(fā)送的消息,會在主線程去處理盯桦。
然后會 new 一個 PendingPostQueue, 這個 PendingPostQueue 是一個 PendingPost 類型的隊列澡绩。
而 PendingPost 則是 訂閱者和事件信息實(shí)體類,并含有同一隊列中指向下一個對象的指針俺附。通過緩存存儲不用的對象,減少下次創(chuàng)建的性能消耗溪掀。
會看的很懵事镣,其實(shí)很好理解, PendingPostQueue就相當(dāng)于 MessageQueue, PendingPost 則相當(dāng)于 Message璃哟。當(dāng)然只是類比氛琢,在調(diào)用 enqueue 的時候,會發(fā)送一個空的 Message随闪,如下代碼:
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
可以看到我們把 pendingPost 放入到 PendingPostQueue阳似, 然后發(fā)送一個空的 Message, 我們理解 Handler 機(jī)制铐伴,所以去看 handleMessage 吧撮奏。
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
代碼很長,但是你可以很清楚的看到這句代碼 eventBus.invokeSubscriber(pendingPost);
還記得 invokeSubscriber 方法嗎当宴,沒錯就是 通過反射執(zhí)行 method畜吊。 當(dāng)然這里重載了該 方法, 但是最終還是會走到 那個有兩個參數(shù)的 invokeSubscriber 方法户矢。
到這里玲献,我們的 MAIN 狀態(tài)的 post 流程也走完了。
3.3 BACKGROUND 和 ASYNC
這兩個狀態(tài)希望大家自己去看源碼了梯浪,并沒有什么復(fù)雜的捌年,無非就是 backgroundPoster 和 asyncPoster 對線程處理的不同,這兩個 Poster 內(nèi)部同樣有 PendingPostQueue 挂洛。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
這是 AsyncPoster 和 BackgroundPoster 內(nèi)部的線程池 在 EventBusBuilder 中的定義礼预。