【Android】EventBus 源碼解析

EventBus 源碼解析

本文爲 Android 開源項目實現原理解析 中 EventBus 部分
項目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo
分析者:Trinea,校對者:扔物線,校對狀態:未完成java

1. 功能介紹

1.1 EventBus

EventBus 是一個 Android 事件發佈/訂閱框架,經過解耦發佈者和訂閱者簡化 Android 事件傳遞,這裏的事件能夠理解爲消息,本文中統一稱爲事件。事件傳遞既可用於 Android 四大組件間通信,也能夠用戶異步線程和主線程間通信等等。
傳統的事件傳遞方式包括:Handler、BroadCastReceiver、Interface 回調,相比之下 EventBus 的優勢是代碼簡潔,使用簡單,並將事件發佈和訂閱充分解耦。android

1.2 概念

事件(Event):又可稱爲消息,本文中統一用事件表示。其實就是一個對象,能夠是網絡請求返回的字符串,也能夠是某個開關狀態等等。事件類型(EventType)指事件所屬的 Class。
事件分爲通常事件和 Sticky 事件,相對於通常事件,Sticky 事件不一樣之處在於,當事件發佈後,再有訂閱者開始訂閱該類型事件,依然能收到該類型事件最近一個 Sticky 事件。
訂閱者(Subscriber):訂閱某種事件類型的對象。當有發佈者發佈這類事件後,EventBus 會執行訂閱者的 onEvent 函數,這個函數叫事件響應函數。訂閱者經過 register 接口訂閱某個事件類型,unregister 接口退訂。訂閱者存在優先級,優先級高的訂閱者能夠取消事件繼續向優先級低的訂閱者分發,默認全部訂閱者優先級都爲 0。
發佈者(Publisher):發佈某事件的對象,經過 post 接口發佈事件。git

2. 整體設計

本項目較爲簡單,整體設計請參考3.1 訂閱者、發佈者、EventBus 關係圖4.1 類關係圖github

3. 流程圖

3.1 訂閱者、發佈者、EventBus 關係圖

eventbus img
EventBus 負責存儲訂閱者、事件相關信息,訂閱者和發佈者都只和 EventBus 關聯。緩存

3.2 事件響應流程

eventbus img
訂閱者首先調用 EventBus 的 register 接口訂閱某種類型的事件,當發佈者經過 post 接口發佈該類型的事件時,EventBus 執行調用者的事件響應函數。性能優化

4. 詳細設計

4.1 類關係圖

eventbus img
以上是 EventBus 主要類的關係圖,從中咱們也能夠看出大部分類都與 EventBus 直接關聯。上部分主要是訂閱者相關信息,中間是 EventBus 類,下面是 發佈者發佈事件後的調用,具體類的功能請看下面的詳細介紹。網絡

4.2 核心類功能介紹

4.2.1 EventBus.jva

EventBus 類負責全部對外暴露的 API,其中的 register、post、unregister 函數配合上自定義的 EventType 及事件響應函數便可完成核心功能,見 3.2 圖。
EventBus 默承認經過靜態函數 getDefault 獲取單例,固然有須要也能夠經過 EventBusBuilder 或 構造函數新建一個 EventBus,每一個新建的 EventBus 發佈和訂閱事件都是相互隔離的,即一個 EventBus 對象中的發佈者發佈事件,另外一個 EventBus 對象中的訂閱者不會收到該訂閱。
EventBus 中對外 API,主要包括兩類:框架

(1) register 和 unregister

分別表示訂閱事件和取消訂閱。register 最底層函數有三個參數,分別爲訂閱者對象、是不是 Sticky 事件、優先級。異步

private synchronized void register(Object subscriber, boolean sticky, int priority)

PS:在此以前的版本 EventBus 還容許自定義事件響應函數名稱,這版本中此功能已經被去除。
register 函數流程圖以下: eventbus img
register 函數中會先根據訂閱者類名去subscriberMethodFinder中查找當前訂閱者全部事件響應函數,而後循環每個事件響應函數,依次執行下面的 subscribe 函數:async

(2) subscribe

subscribe 函數分三步
第一步:經過subscriptionsByEventType獲得該事件類型全部訂閱者信息隊列,根據優先級將當前訂閱者信息插入到訂閱者隊列subscriptionsByEventType中;
第二步:在typesBySubscriber中獲得當前訂閱者訂閱的全部事件隊列,將此事件保存到隊列typesBySubscriber中,用於後續取消訂閱;
第三步:檢查這個事件是不是 Sticky 事件,若是是則從stickyEvents事件保存隊列中取出該事件類型最後一個事件發送給當前訂閱者。

(3) post、cancel 、removeStickEvent

post 函數用於發佈事件,cancel 函數用於取消某訂閱者訂閱的全部事件類型、removeStickEvent 函數用於刪除 sticky 事件。
post 函數流程圖以下: eventbus img
post 函數會首先獲得當前線程的 post 信息PostingThreadState,其中包含事件隊列,將當前事件添加到其事件隊列中,而後循環調用 postSingleEvent 函數發佈隊列中的每一個事件。
postSingleEvent 函數會先去eventTypesCache獲得該事件對應類型的的父類及接口類型,沒有緩存則查找並插入緩存。循環獲得的每一個類型和接口,調用 postSingleEventForEventType 函數發佈每一個事件到每一個訂閱者。
postSingleEventForEventType 函數在subscriptionsByEventType查找該事件訂閱者訂閱者隊列,調用 postToSubscription 函數向每一個訂閱者發佈事件。
postToSubscription 函數中會判斷訂閱者的 ThreadMode,從而決定在什麼 Mode 下執行事件響應函數,具體以下:

a. 若是是`PostThread`,則直接調用訂閱者的事件響應函數;  
b. 若是是`MainThread`而且發佈線程就是主線程,則直接調用訂閱者的事件響應函數,不然經過主線程的 Handler 發送消息在主線程中處理——調用訂閱者的事件響應函數;  
c. 若是是`BackgroundThread`而且發佈線程是主線程,則啓動異步線程去處理,不然直接直接調用訂閱者的事件響應函數;  
d. 若是是`Async`,則啓動異步線程去處理——調用訂閱者的事件響應函數。  

(4) 主要成員變量含義

1.defaultInstance默認的 EventBus 實例,根據EventBus.getDefault()函數獲得。
2.DEFAULT_BUILDER默認的 EventBus Builder。
3.eventTypesCache事件對應類型及其父類和實現的接口的緩存,以 eventType 爲 key,元素爲 Object 的 ArrayList 爲 Value,Object 對象爲 eventType 的父類或接口。 4.subscriptionsByEventType事件訂閱者的保存隊列,以 eventType 爲 key,元素爲Subscription的 ArrayList 爲 Value,其中Subscription爲訂閱者信息,由 subscriber, subscriberMethod, priority 構成。
5.typesBySubscriber訂閱者訂閱的事件的保存隊列,以 subscriber 爲 key,元素爲 eventType 的 ArrayList 爲 Value。
6.stickyEventsSticky 事件保存隊列,以 eventType 爲 key,event 爲元素,由此能夠看出對於同一個 eventType 最多隻會有一個 event 存在。
7.currentPostingThreadState當前線程的 post 信息,包括事件隊列、是否正在分發中、是否在主線程、訂閱者信息、事件實例、是否取消。
8.mainThreadPosterbackgroundPosterasyncPoster事件主線程處理者、事件 Background 處理者、事件異步處理者。
9.subscriberMethodFinder訂閱者響應函數信息存儲和查找類。
10.executorService異步和 BackGround 處理方式的線程池。
11.throwSubscriberException當調用事件處理函數異常時是否拋出異常,默認爲 false,建議經過

EventBus.builder().throwSubscriberException(true).installDefaultEventBus()

打開。
12.logSubscriberExceptions當調用事件處理函數異常時是否打印異常信息,默認爲 true。
13.logNoSubscriberMessages當沒有訂閱者訂閱該事件時是否打印日誌,默認爲 true。
14.sendSubscriberExceptionEvent當調用事件處理函數異常時是否發送 SubscriberExceptionEvent 事件,若此開關打開,訂閱者可經過

public void onEvent(SubscriberExceptionEvent event) 

訂閱該事件進行處理,默認爲 true。 15.sendNoSubscriberEvent當沒有事件處理函數對事件處理時是否發送 NoSubscriberEvent 事件,若此開關打開,訂閱者可經過

public void onEvent(NoSubscriberEvent event)

訂閱該事件進行處理,默認爲 true。
16.eventInheritance是否支持事件繼承,默認爲 true。

4.2.2 EventBusBuilder.java

跟通常 Builder 相似,用於在須要設置參數過多時構造 EventBus。包含的屬性也是 EventBus 的一些設置參數,意義見4.2.1 EventBus.java的介紹,build 函數用於新建 EventBus 對象,installDefaultEventBus 函數將當前設置應用於 Default EventBus。

4.2.3 SubscriberMethodFinder.java

訂閱者響應函數信息存儲和查找類,由 HashMap 緩存,以 ${subscriberClassName} 爲 key,SubscriberMethod 對象爲元素的 ArrayList 爲 value。findSubscriberMethods 函數用於查找訂閱者響應函數,若是不在緩存中,則遍歷本身的每一個函數並遞歸父類查找,查找成功後保存到緩存中。遍歷及查找規則爲:
a. 遍歷 subscriberClass 每一個方法;
b. 該方法不以java.javax.android.這些 SDK 函數開頭,並以 ${eventMethodName} 開頭,表示多是事件響應函數繼續,不然檢查下一個方法;
c. 該方法是不是 public 的,而且不是 ABSTRACT、STATIC、BRIDGE、SYNTHETIC 修飾的,知足條件則繼續。其中 BRIDGE、SYNTHETIC 爲編譯器生成的一些函數修飾符;
d. 該方法是否只有 1 個參數,知足條件則繼續;
e. 該方法名爲 ${eventMethodName} 則 threadMode 爲ThreadMode.PostThread
該方法名爲 ${eventMethodName}MainThread 則 threadMode 爲ThreadMode.MainThread
該方法名爲 ${eventMethodName}BackgroundThread 則 threadMode 爲ThreadMode.BackgroundThread
該方法名爲 ${eventMethodName}Async 則 threadMode 爲ThreadMode.Async
其餘狀況且不在忽略名單 (skipMethodVerificationForClasses) 中則拋出異常。
f. 獲得該方法惟一的參數即事件類型 eventType,將這個方法、threadMode、eventType 一塊兒構造 SubscriberMethod 對象放到 ArrayList 中。
g. 回到 b 遍歷 subscriberClass 的下一個方法,若方法遍歷結束到 h; h. 回到 a 遍歷本身的父類,若父類遍歷結束回到 i;
i. 若 ArrayList 依然爲空則拋出異常,不然會將 ArrayList 作爲 value,${subscriberClassName} 作爲 key 放到緩存 HashMap 中。 對於事件函數的查找有兩個小的性能優化點:

a. 第一次查找後保存到了緩存中,即上面介紹的 HashMap  
b. 遇到 java. javax. android. 開頭的類會自動中止查找  

類中的 skipMethodVerificationForClasses 屬性表示跳過哪些類中非法以 {eventMethodName} 開頭的函數檢查,若不跳過澤輝拋出異常。
PS:在此以前的版本 EventBus 容許自定義事件響應函數名稱,緩存的 HashMap key 爲 ${subscriberClassName}.${eventMethodName},這版本中此功能已經被去除。

4.2.4 SubscriberMethod.java

訂閱者事件響應函數信息,包括響應方法、線程 Mode、事件類型以及一個用來比較 SubscriberMethod 是否相等的特徵值 methodString 共四個變量,其中 methodString 爲 ${methodClassName}#${methodName}(${eventTypeClassName}。

4.2.5 Subscription.java

訂閱者信息,包括 subscriber 對象、事件響應方法 SubscriberMethod、優先級 priority。

4.2.6 HandlerPoster.jva

事件主線程處理,對應ThreadMode.MainThread。繼承自 Handler,enqueue 函數將事件放到隊列中,並利用 handler 發送 message,handleMessage 函數從隊列中取事件,invoke 事件響應函數處理。

4.2.7 AsyncPoster.java

事件異步線程處理,對應ThreadMode.Async,繼承自 Runnable。enqueue 函數將事件放到隊列中,並調用線程池執行當前任務,在 run 函數從隊列中取事件,invoke 事件響應函數處理。

4.2.8 BackgroundPoster.java

事件 Background 處理,對應ThreadMode.BackgroundThread,繼承自 Runnable。enqueue 函數將事件放到隊列中,並調用線程池執行當前任務,在 run 函數從隊列中取事件,invoke 事件響應函數處理,與 AsyncPoster.java 不一樣的是這裏會循環等待 run,還沒有想清楚緣由。

4.2.9 PendingPost.java

訂閱者和事件信息實體類,並含有同一隊列中指向下一個對象的指針。經過緩存存儲不用的對象,減小下次建立的性能消耗。

4.2.10 PendingPostQueue.java

經過 head 和 tail 指針維護一個PendingPost隊列。HandlerPoster、AsyncPoster、BackgroundPoster 都包含一個此隊列實例,表示各自的訂閱者及事件信息隊列,在事件到來時進入隊列,處理時從隊列中取出一個元素進行處理。

4.2.11 SubscriberExceptionEvent.java

當調用事件處理函數異常時發送的 EventBus 內部自定義事件,經過 post 發送,訂閱者可自行訂閱這類事件進行處理。

4.2.12 NoSubscriberEvent.java

當沒有事件處理函數對事件處理時發送的 EventBus 內部自定義事件,經過 post 發送,訂閱者可自行訂閱這類事件進行處理。

4.2.13 EventBusException.java

封裝於 RuntimeException 之上的 Exception,只是覆蓋構造函數,至關於一個標記,標記是屬於 EventBus 的 Exception。

4.2.14 ThreadMode.java

線程 Mode 枚舉類,表示事件響應函數執行線程信息,包括ThreadMode.PostThreadThreadMode.MainThreadThreadMode.BackgroundThreadThreadMode.Async四種。

相關文章
相關標籤/搜索