Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android EventBus 源碼解析

Android EventBus 源碼解析

編輯:關於Android編程

1. 功能介紹

1.1 EventBus

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

1.2 概念

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

2. 總體設計

本項目較為簡單,總體設計請參考3.1 訂閱者、發布者、EventBus 關系圖及4.1 類關系圖。

3. 流程圖

3.1 訂閱者、發布者、EventBus 關系圖

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

3.2 事件響應流程

訂閱者首先調用 EventBus 的 register 接口訂閱某種類型的事件,當發布者通過 post 接口發布該類型的事件時,EventBus 執行調用者的事件響應函數。

4. 詳細設計

4.1 類關系圖

以上是 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 函數:

(2) subscribe

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

(3) post、cancel 、removeStickEvent

post 函數用於發布事件,cancel 函數用於取消某訂閱者訂閱的所有事件類型、removeStickEvent 函數用於刪除 sticky 事件。

post 函數流程圖如下:
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.mainThreadPoster、backgroundPoster、asyncPoster事件主線程處理者、事件 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 為 ${methodClassNmae}#${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.PostThread、ThreadMode.MainThread、ThreadMode.BackgroundThread、ThreadMode.Async四種。

5. 與 Otto 對比

等 Otto 分析完成

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved