EventBus用法及源碼解析
目錄介紹
1.EventBus簡介
1.1 EventBus的三要素
1.2 EventBus的四種ThreadMode(線程模型)
1.3 EventBus怎麼調用
2.EventBus使用
2.1 最簡單的使用
3.EventBus注冊源碼解析
3.1 EventBus.getDefault()擷取對象
3.2 register(this)注冊源碼解析
3.2.1 首先看register(this)源碼
3.2.2 接下來看findSubscriberMethods(subscriberClass)裡面的源碼
3.2.3 接下來看findUsingInfo(subscriberClass)源碼
3.3 查找完所有的訂閱方法後便開始對所有的訂閱方法進行注冊
3.3.1 subscribe(subscriber, subscriberMethod);
3.3.2 訂閱者的注冊過程
3.3.3 流程圖
4.EventBus事件分發解析
4.1 從post方法入手
4.2 什麼是PostingThreadState?
4.3 PostingThreadState怎麼獲得?
4.4 來看看postSingleEvent方法裡做了什麼
4.5 接下來看看postSingleEventForEventType方法
4.6 接下來看看postToSubscription方法
4.7 整個流程圖
4.8 總結一下整個事件分發的過程
5.EventBus取消注冊解析
5.1 unregister(this)方法入手
5.2 再來看看unsubscribeByEventType(subscriber, eventType)
5.3 取消注冊流程圖
5.4 總結一下取消注冊的過程
6.總結一下EventBus的工作原理
6.1 訂閱邏輯
6.2 事件發送邏輯
6.3 取消邏輯
6.4 利與弊
7.其他介紹
7.1 參考文檔
7.2 其他
0.本人寫的綜合案例
<a href="https://github.com/yangchong211/LifeHelper">案例</a>
<a href="https://github.com/yangchong211/LifeHelper/blob/master/README.md">說明及截圖</a>
子產品:新聞,音樂,視訊,圖檔,唐詩宋詞,快遞,天氣,記事本,閱讀器等等
接口:七牛,阿裡雲,天行,幹貨集中營,極速資料,追書神器等等
Event:事件
可以是任意類型的對象。
Subscriber:事件訂閱者
在EventBus3.0之前,消息處理的方法隻能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,他們分别代表四種線程模型。
在EventBus3.0之後,事件處理的方法可以随便取名,但是需要添加一個注解@Subscribe,并且要指定線程模型(預設為POSTING),四種線程模型下面會講到。
Publisher:事件釋出者
可以在任意線程任意位置發送事件,直接調用EventBus的post(Object)方法。可以自己執行個體化EventBus對象,但一般使用EventBus.getDefault()就好了,根據post函數參數的類型,會自動調用訂閱相應類型事件的函數。
POSTING(預設):
如果使用事件處理函數指定了線程模型為POSTING,那麼該事件在哪個線程釋出出來的,事件處理函數就會在這個線程中運作,也就是說釋出事件和接收事件在同一個線程。線上程模型為POSTING的事件處理函數中盡量避免執行耗時操作,因為它會阻塞事件的傳遞,甚至有可能會引起ANR。
MAIN:
事件的處理會在UI線程中執行。事件處理時間不能太長,長了會ANR的。
BACKGROUND:
如果事件是在UI線程中釋出出來的,那麼該事件處理函數就會在新的線程中運作,如果事件本來就是子線程中釋出出來的,那麼該事件處理函數直接在釋出事件的線程中執行。在此事件處理函數中禁止進行UI更新操作。
ASYNC:
無論事件在哪個線程釋出,該事件處理函數都會在建立的子線程中執行,同樣,此事件處理函數中禁止進行UI更新操作。
1.3 EventBus怎麼調用**
代碼如下: EventBus.getDefault().post(param);
調用原理簡單了解為:
一句話,你也可以叫釋出,隻要把這個param釋出出去,EventBus會在它内部存儲的方法中,進行掃描,找到參數比對的,就使用反射進行調用。
撇開專業術語:其實EventBus就是在内部存儲了一堆onEvent開頭的方法,然後post的時候,根據post傳入的參數,去找到比對的方法,反射調用之。
它内部使用了Map進行存儲,鍵就是參數的Class類型。知道是這個類型,那麼你覺得根據post傳入的參數進行查找還是個事麼?
2.1.1 自定義一個事件類
2.1.2 在需要訂閱事件的地方注冊事件
2.1.3 發送事件
2.1.4 處理事件
3.0之後, 消息處理的方法可以随便取名
問題: (threadMode = ThreadMode.MAIN)是做什麼用的??
需要添加一個注解@Subscribe,并且要指定線程模型
如果沒有添加,那就是預設為POSTING
2.1.5 取消事件訂閱
a.先看源碼:
b.分析
單例模式, 使用了雙重判斷的方式,防止并發的問題,還能極大的提高效率
3.2.1.首先看register(this)源碼
3.2.2.接下來看findSubscriberMethods(subscriberClass)裡面的源碼
該方法的作用其實就是從訂閱類中擷取所有的訂閱方法資訊
findSubscriberMethods找出一個SubscriberMethod的集合,也就是傳進來的訂閱者所有的訂閱的方法,接下來周遊訂閱者的訂閱方法來完成訂閱者的訂閱操作。對于SubscriberMethod(訂閱方法)類中,主要就是用儲存訂閱方法的Method對象、線程模式、事件類型、優先級、是否是粘性事件等屬性。
源碼分析:首先從緩存中查找,如果找到了就立馬傳回。如果緩存中沒有的話,則根據 ignoreGeneratedIndex 選擇如何查找訂閱方法,ignoreGeneratedIndex屬性表示是否忽略注解器生成的MyEventBusIndex。最後,找到訂閱方法後,放入緩存,以免下次繼續查找。ignoreGeneratedIndex 預設就是false,可以通過EventBusBuilder來設定它的值。我們在項目中經常通過EventBus單例模式來擷取預設的EventBus對象,也就是ignoreGeneratedIndex為false的情況,這種情況調用了findUsingInfo方法
通過getSubscriberInfo方法來擷取訂閱者資訊。在我們開始查找訂閱方法的時候并沒有忽略注解器為我們生成的索引MyEventBusIndex,如果我們通過EventBusBuilder配置了MyEventBusIndex,便會擷取到subscriberInfo,調用subscriberInfo的getSubscriberMethods方法便可以得到訂閱方法相關的資訊,這個時候就不在需要通過注解進行擷取訂閱方法。如果沒有配置MyEventBusIndex,便會執行findUsingReflectionInSingleClass方法,将訂閱方法儲存到findState中。最後再通過getMethodsAndRelease方法對findState做回收處理并反回訂閱方法的List集合。
3.3.1 回到
register(this)這個方法

訂閱的代碼主要就做了兩件事,第一件事是将我們的訂閱方法和訂閱者封裝到subscriptionsByEventType和typesBySubscriber中,subscriptionsByEventType是我們投遞訂閱事件的時候,就是根據我們的EventType找到我們的訂閱事件,進而去分發事件,處理事件的;typesBySubscriber在調用unregister(this)的時候,根據訂閱者找到EventType,又根據EventType找到訂閱事件,進而對訂閱者進行解綁。第二件事,如果是粘性事件的話,就立馬投遞、執行。
首先從PostingThreadState對象中取出事件隊列,然後再将目前的事件插入到事件隊列當中。最後将隊列中的事件依次交由postSingleEvent方法進行處理,并移除該事件。
PostingThreadState中包含了目前線程的事件隊列,就是目前線程所有分發的事件都儲存在eventQueue事件隊列中以及訂閱者訂閱事件等資訊,有了這些資訊我們就可以從事件隊列中取出事件分發給對應的訂閱者
ThreadLocal 是一個線程内部的資料存儲類,通過它可以在指定的線程中存儲資料,而這段資料是不會與其他線程共享的。
可以看出currentPostingThreadState的實作是一個包含了PostingThreadState的ThreadLocal對象,這樣可以保證取到的都是自己線程對應的資料。
我們有了PostingThreadState擷取到了目前線程的事件隊列,接下來就是事件分發,我們來看postSingleEvent(eventQueue.remove(0), postingState);
eventInheritance表示是否向上查找事件的父類,它的預設值為true,可以通過在EventBusBuilder中來進行配置。當eventInheritance為true時,則通過lookupAllEventTypes找到所有的父類事件并存在List中,然後通過postSingleEventForEventType方法對事件逐一處理,接下來看看postSingleEventForEventType方法
同步取出該事件對應的Subscription集合并周遊該集合将事件event和對應Subscription傳遞給postingState并調用postToSubscription方法對事件進行處理,接下來看看postToSubscription方法:
首先擷取目前線程的PostingThreadState對象進而擷取到目前線程的事件隊列
通過事件類型擷取到所有訂閱者集合
通過反射執行訂閱者中的訂閱方法
1、首先擷取訂閱者的所有訂閱事件
2、周遊訂閱事件
3、根據訂閱事件擷取所有的訂閱了該事件的訂閱者集合
4、将該訂閱者移除
5、将步驟1中的集合中的訂閱者移除
1、首先用register()方法注冊一個訂閱者
2、擷取該訂閱者的所有訂閱的方法
3、根據該訂閱者的所有訂閱的事件類型,将訂閱者存入到每個以 事件類型為key 以所有訂閱者為values的map集合中
4、然後将訂閱事件添加到以訂閱者為key 以訂閱者所有訂閱事件為values的map集合中
5、如果是訂閱了粘滞事件的訂閱者,從粘滞事件緩存區擷取之前發送過的粘滞事件,響應這些粘滞事件。
1、首先擷取目前線程的事件隊列
2、将要發送的事件添加到事件隊列中
3、根據發送事件類型擷取所有的訂閱者
4、根據響應方法的執行模式,在相應線程通過反射執行訂閱者的訂閱方法
1、首先通過unregister方法拿到要取消的訂閱者
2、得到該訂閱者的所有訂閱事件類型
3、周遊事件類型,根據每個事件類型擷取到所有的訂閱者集合,并從集合中删除該訂閱者
4、将訂閱者從步驟2的集合中移除
EventBus好處比較明顯,它能夠解耦和,将業務和視圖分離,代碼實作比較容易。而且3.0後,我們可以通過apt預編譯找到訂閱者,避免了運作期間的反射處了解析,大大提高了效率。當然EventBus也會帶來一些隐患和弊端,如果濫用的話會導緻邏輯的分散并造成維護起來的困難。另外大量采用EventBus代碼的可讀性也會變差。
脈脈:yc930211