1. 程式人生 > >EventBus初理解

EventBus初理解

 緣由:     平時工作,因為懶於動筆的原因,也沒注重技術和經驗的積累,導致之前曾經研究過的問題現在又忘記了,所以要慢慢注重積累,那麼就從寫作開始,談談對工作中碰到的問題進行整理和歸納。     我們都知道,在Android中,想處理事件傳遞,可以用Handler+MessageQueue+Message+Looper迴圈,固然是有解決方法,但是這個使用起來不方便,程式碼寫起來也不簡潔,同時還必須要理解好Handler+MessageQueue+Message+Looper之間的關係,比如這樣的圖: 是不是看到覺得頭大,理解起來也麻煩,但是這個原理是很有必要去了解的,這個也是基本功之一吧。今天我們來講講對於EventBus這個開源庫的分析,看看高手程式碼是如何寫的,如何解耦合,對於自身的技術的進步相比也是很有大的幫助吧。     初認識:
 因為在工作大量用到Fragment和Activity之間的耦合,需要獲取對方某某的例項,自然而然就去尋找事件之間是否有解耦合的庫,EventBus就湧現出來了,這個是greenrobot大神寫的,多麼經典已經無需多說了,github地址:https://github.com/greenrobot/EventBus,雖然我們會使用,但是也需要了解裡面的原理,這樣使用的起來也放心,畢竟對於陌生的東西不明白內部情況,就去用,什麼時候被坑也會不知道的。     腦補下:     介紹這個庫之前,需要認識以下幾個名詞。

    事件(Event):又可稱為訊息,本文中統一用事件表示。其實就是一個物件,可以是網路請求返回的字串,也可以是某個開關狀態等等。事件型別(EventType)指事件所屬的 Class。事件分為一般事件和 Sticky 事件,相對於一般事件,Sticky 事件不同之處在於,當事件釋出後,再有訂閱者開始訂閱該型別事件,依然能收到該型別事件最近一個 Sticky 事件。

    訂閱者(Subscriber):訂閱某種事件型別的物件。當有釋出者釋出這類事件後,EventBus 會執行訂閱者的 onEvent 函式,這個函式叫事件響應函式。訂閱者通過 register 介面訂閱某個事件型別,unregister 介面退訂。訂閱者存在優先順序,優先順序高的訂閱者可以取消事件繼續向優先順序低的訂閱者分發,預設所有訂閱者優先順序都為 0。

    釋出者(Publisher):釋出某事件的物件,通過 post 介面釋出事件。

    結構圖:          從中,我們可以看到這個庫整體結構是基於生產者/消費者模式,也可以稱呼為釋出者/訂閱者,顯得更親切。所謂的釋出者/訂閱者,比如我們日常生活中,小區的通告欄,生產者類似小區居委會,訂閱者就是小區裡的住戶,當你需要小區居委會幫忙的時候,比如你外出了,可以叫居委會裡某個大媽幫你拿下快遞,那麼你需要在居委會那裡登記,也可以叫做註冊,只有登記了,居委會裡的大媽才知道你是我們同一個小區的,也可以認識你了。同時,每當有事情發生,需要通知的時候,需要在通告欄裡釋出一個通告,現在資訊發達了,通告欄可以通過微信公眾號來發布,但是也需要每個人去掃一掃,關注下小區的公眾號,一發通知,有關注的業主自然就知道通知事情是什麼。那麼這個EventBus庫的實現原理,跟我們日常生活中的微信公眾號很型別,首先在使用的時候,我們需要向公眾平臺註冊,註冊完之後, 當生產者需要釋出資訊的時候,平臺會幫我們把這些訊息推送給訂閱者,訂閱者根據訊息內容,進行不同處理操作。    使用方法流程:     
    類圖:     eventbus img 根據類來分析,從中,我們可以看到EventBus依賴有五個類,分別如下。      SubscriberMethod:這是一個訂閱方法類的封裝,包含了方法反射的名稱,執行緒模型和事件型別,比如當你向主幹註冊的時候,這時候就會例項化SubscriberMethod類例項,來存放以onEvent開頭的方法,執行的執行緒模型和自定義的傳遞事件型別。      SubscriberMethodFinder:在這個類中,我們可以看到為何在定義方法訊息接受回撥時,會以“onEvent”開頭的方法,因為這裡有個
private static final String ON_EVENT_METHOD_NAME = "onEvent";
ON_EVENT_METHOD_NAME的常量。裡面關鍵方法是findSubscriberMethods(),具體實現可以去看程式碼,通過反射的方法,找出訂閱者上的以onEvent開頭的方法,最終返回的時候SubscriberMethod類的集合,也就是所有事件響應函式。  HandlerPoster:這是繼承Handler的請求類,封裝了請求佇列,整個過程是在佇列不斷髮送請求,直到所有的請求都出佇列,也就是全部發送完畢。事件主執行緒處理,對應ThreadMode.MainThread。enqueue 函式將事件放到佇列中,並利用 handler 傳送 message,handleMessage 函式從佇列中取事件,invoke 事件響應函式處理。    AsyncPoster:這個其實一個Runnable實現類,封裝執行的過程,在非同步時呼叫。是一個事件非同步執行緒處理,對應ThreadMode.Async。enqueue 函式將事件放到佇列中,並呼叫執行緒池執行當前任務,在 run 函式從佇列中取事件,invoke 事件響應函式處理。    BackgroundPoster:這個其實一個Runnable實現類,事件 Background 處理,對應ThreadMode.BackgroundThread。enqueue 函式將事件放到佇列中,並呼叫執行緒池執行當前任務,在 run 函式從佇列中取事件,invoke 事件響應函式處理。與 AsyncPoster.java 不同的是BackgroundPoster 中的任務只在同一個執行緒中依次執行,而不是併發執行。

    ThreadMode模型

    執行緒模型共有四類:
  • PostThread,預設的ThreadMode,表示在執行Post操作的執行緒直接呼叫訂閱者的事件響應方法不論該執行緒是否為主執行緒(UI執行緒)。當該執行緒為主執行緒時,響應方法不能有耗時操作,否則有卡主執行緒風險。適用場景:對於是否在主執行緒執行無要求,但若 Post 執行緒為主執行緒,不能耗時的操作。
  • MainThread,在主執行緒中執行響應方法。如果釋出執行緒就是主執行緒,則直接呼叫訂閱者的事件響應方法,否則通過主執行緒的 Handler 傳送訊息在主執行緒中處理——呼叫訂閱者的事件響應函式。顯然,MainThread類的方法也不能有耗時操作,以避免卡主執行緒。適用場景:必須在主執行緒執行的操作;
  • BackgroundThread,在後臺執行緒中執行響應方法。如果釋出執行緒不是主執行緒,則直接呼叫訂閱者的事件響應函式,否則啟動唯一的後臺執行緒去處理。由於後臺執行緒是唯一的,當事件超過一個的時候,它們會被放在佇列中依次執行,因此該類響應方法雖然沒有PostThread類和MainThread類方法對效能敏感,但最好不要有重度耗時的操作或太頻繁的輕度耗時操作,以造成其他操作等待。適用場景:操作輕微耗時且不會過於頻繁,即一般的耗時操作都可以放在這裡;
  • Async,不論釋出執行緒是否為主執行緒,都使用一個空閒執行緒來處理。和BackgroundThread不同的是,Async類的所有執行緒是相互獨立的,因此不會出現卡執行緒的問題。適用場景:長耗時操作,例如網路訪問。
   EventBus類介紹     EventBus類是對外暴露的API,包括register(),post(),unregister()方法,配合自定義的EventTypeji s事件響應,即可完成使用過程。     EventBus類有個預設getDefault單例,當然也可以通過EventBusBuilder 或建構函式新建一個EventBus,每個新建的EventBus釋出和訂閱事件都是相互隔離的,即在一個EventBus物件中釋出事件,如果有另外一個EventBus物件,則另外一個EventBus物件的訂閱者不會收到該訂閱。     關鍵點:      1,register和unregister,分別表示訂閱事件和取消訂閱,register 最底層函式有三個引數,分別為訂閱者物件、是否是 Sticky 事件、優先順序。 註冊流程:     

register 函式中會先根據訂閱者類名去subscriberMethodFinder中查詢當前訂閱者所有事件響應函式,然後迴圈每一個事件響應函式,依次執行下面的 subscribe 函式:

第一,通過subscriptionsByEventType得到該事件型別的所有訂閱者資訊佇列,根據優先順序把當前訂閱者資訊插入到訂閱者佇列subscriptionsByEventType中。

第二,在typesBySubscriber中得到當前訂閱者的所有事件佇列,將此事件儲存到佇列typesBySubscriber中,用於後續取消訂閱。

第三,檢查這個事件是否是 Sticky 事件,如果是則從stickyEvents事件儲存佇列中取出該事件型別最後一個事件傳送給當前訂閱者。 釋出流程: post方法,用於釋出事件,cancle方法使用者取消訂閱者訂閱的所有事件型別。

post 方法會首先得到當前執行緒的 post 資訊PostingThreadState,其中包含事件佇列,將當前事件新增到其事件佇列中,然後迴圈呼叫 postSingleEvent 函式釋出佇列中的每個事件。

postSingleEvent 方法會先去eventTypesCache得到該事件對應型別的的父類及介面型別,沒有快取則查詢並插入快取。迴圈得到的每個型別和介面,呼叫 postSingleEventForEventType 方法釋出每個事件到每個訂閱者。

postSingleEventForEventType 方法在subscriptionsByEventType查詢該事件訂閱者訂閱者佇列,呼叫 postToSubscription 函式向每個訂閱者釋出事件。

結尾:

重點名稱解釋:
  • typesBySubscriber訂閱者訂閱的事件的儲存佇列,以 subscriber 為 key,元素為 eventType 的 ArrayList 為 Value。
  • currentPostingThreadState當前執行緒的 post 資訊,包括事件佇列、是否正在分發中、是否在主執行緒、訂閱者資訊、事件例項、是否取消。
  • mainThreadPoster、backgroundPoster、asyncPoster事件主執行緒處理者、事件 Background 處理者、事件非同步處理者。
  • subscriberMethodFinder訂閱者響應函式資訊儲存和查詢類。
  • executorService非同步和 BackGround 處理方式的執行緒池。