Android 淺析 EventBus (二) 原理

Android 淺析 EventBus (二) 原理


前言

Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code

概括

本次分析從兩個(gè)方向深入,一個(gè)是從注冊(cè)開始,一個(gè)是從發(fā)送消息開始。從這兩個(gè)方向就能大致了解eventbus的運(yùn)作原理。

注冊(cè)原理

register

MainActivity

EventBus.getDefault().register(this);
EventBus的注冊(cè)就從這里開始。

Step 1.EventBus.getDefault()

public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

這是一句很典型的單例寫法,整個(gè)eventbus在項(xiàng)目中是以單例的形式出現(xiàn)的。在初始化這塊調(diào)用的是EventBusBuilder的默認(rèn)參數(shù)。這也是Builder模式比較常用的。

Step 2.EventBus.register(Object subscriber)

這里是注冊(cè)訂閱者的地方,同樣的注冊(cè)方式有這么幾個(gè):

  1. register(Object subscriber)
  2. register(Object subscriber, int priority)
  3. registerSticky(Object subscriber)
  4. registerSticky(Object subscriber, int priority)
    它們之間最主要的區(qū)別就是參數(shù)的不同。在實(shí)現(xiàn)上它們都是調(diào)用void register(Object subscriber, boolean sticky, int priority)函數(shù)實(shí)現(xiàn)的。
private synchronized void register(Object subscriber, boolean sticky, int priority) {
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod, sticky, priority);
    }
}

這里首先調(diào)用findSubscriberMethods()方法根據(jù)當(dāng)前訂閱者的類名查找到該類的所有訂閱者函數(shù)。

在獲取完所有訂閱者函數(shù)后調(diào)用subscribe方法。

Step 3.EventBus.subscribe(subscriber, subscriberMethod, ...)

這里是注冊(cè)函數(shù)的核心,分成三個(gè)部分:

  1. 通過(guò)subscriptionsByEventType得到這個(gè)Event類型所有訂閱者信息隊(duì)列,然后根據(jù)優(yōu)先級(jí)將當(dāng)前訂閱者信息插入到隊(duì)列里面。
  2. typesBySubscriber中得到當(dāng)前訂閱者訂閱的所有事件隊(duì)列,將此事件保存到隊(duì)列中,用于后續(xù)取消訂閱。
  3. 檢查這個(gè)事件是否是 Sticky 事件,如果是則從stickyEvents事件保存隊(duì)列中取出該事件類型最后一個(gè)事件發(fā)送給當(dāng)前訂閱者。

Step 4.EventBus.unregister(Object subscriber)

最后就是反注冊(cè),這里就比較簡(jiǎn)單了。
首先從typesBySubscriber獲取當(dāng)前訂閱者,然后找到此訂閱者的所有類型,將此訂閱者的所有類型從subscriptionsByEventType表里刪除。接著再把此訂閱者從typesBySubscriber中刪除。

發(fā)送原理

post

MainActivity

public class MessageEvent {
    public final String message;
    public MessageEvent(String message) {
        this.message = message;
    }
 }

public void onEventMainThread(MessageEvent event) {
}

EventBus.getDefault().post(new MessageEvent("hello eventbus"));

EventBus.getDefault().post(this);
EventBus的注冊(cè)就從這里開始。其實(shí)這個(gè)就是一個(gè)序列化和反序列化的過(guò)程。將一段event打包然后再接受函數(shù)解包。

EventBus.post(Object event)

這個(gè)函數(shù)將收到的event發(fā)送到event bus。
首先將此事件保存到currentPostingThreadState的事件隊(duì)列里。

然后查看當(dāng)前是否有事件在發(fā)送,然后調(diào)用postSingleEvent()函數(shù)發(fā)送。

EventBus.postSingleEvent()

首先調(diào)用lookupAllEventTypes()獲取所有事件的類型,然后循環(huán)調(diào)用postSingleEventForEventType()函數(shù)發(fā)送事件。

EventBus.lookupAllEventTypes(...)

這里從當(dāng)前事件中獲取父類和接口,一直往上循環(huán)獲取直到最后。然后保存到eventTypesCache變量里。

EventBus.postSingleEventForEventType(...)

這里就是獲取每個(gè)事件,然后通過(guò)循環(huán)發(fā)送這些事件,會(huì)將事件的參數(shù)添加到PostingThreadState結(jié)構(gòu)體里傳到postToSubscription()函數(shù)來(lái)發(fā)送,這里就能區(qū)分是主界面線程還是非界面線程。最后再把參數(shù)都反初始化。

EventBus.postToSubscription(Subscription subscription, ...)

這個(gè)函數(shù)就是主要的分發(fā)函數(shù),根據(jù)每個(gè)事件的threadMode來(lái)分發(fā)到各自相應(yīng)的回調(diào)函數(shù)。

switch (subscription.subscriberMethod.threadMode) {
    case PostThread:
    case MainThread:
    case BackgroundThread:
    case Async:
}

這里主要就是分發(fā)到四個(gè)不同函數(shù)。

  1. invokeSubscriber()
  2. mainThreadPoster.enqueue()
  3. backgroundPoster.enqueue()
  4. asyncPoster.enqueue()

這里我們分別看下:

  1. PostThread:默認(rèn)的 ThreadMode,表示在執(zhí)行 Post 操作的線程直接調(diào)用訂閱者的事件響應(yīng)方法,不論該線程是否為主線程(UI 線程)。當(dāng)該線程為主線程時(shí),響應(yīng)方法中不能有耗時(shí)操作,否則有卡主線程的風(fēng)險(xiǎn)。適用場(chǎng)景:對(duì)于是否在主線程執(zhí)行無(wú)要求,但若Post線程為主線程,不能耗時(shí)的操作;
  2. MainThread:在主線程中執(zhí)行響應(yīng)方法。如果發(fā)布線程就是主線程,則直接調(diào)用訂閱者的事件響應(yīng)方法,否則通過(guò)主線程的 Handler 發(fā)送消息在主線程中處理——調(diào)用訂閱者的事件響應(yīng)函數(shù)。顯然,MainThread類的方法也不能有耗時(shí)操作,以避免卡主線程。適用場(chǎng)景:必須在主線程執(zhí)行的操作;
  3. BackgroundThread:在后臺(tái)線程中執(zhí)行響應(yīng)方法。如果發(fā)布線程不是主線程,則直接調(diào)用訂閱者的事件響應(yīng)函數(shù),否則啟動(dòng)唯一的后臺(tái)線程去處理。由于后臺(tái)線程是唯一的,當(dāng)事件超過(guò)一個(gè)的時(shí)候,它們會(huì)被放在隊(duì)列中依次執(zhí)行,因此該類響應(yīng)方法雖然沒(méi)有PostThread類和MainThread類方法對(duì)性能敏感,但最好不要有重度耗時(shí)的操作或太頻繁的輕度耗時(shí)操作,以造成其他操作等待。適用場(chǎng)景:操作輕微耗時(shí)且不會(huì)過(guò)于頻繁,即一般的耗時(shí)操作都可以放在這里;
  4. Async:不論發(fā)布線程是否為主線程,都使用一個(gè)空閑線程來(lái)處理。和BackgroundThread不同的是,Async類的所有線程是相互獨(dú)立的,因此不會(huì)出現(xiàn)卡線程的問(wèn)題。適用場(chǎng)景:長(zhǎng)耗時(shí)操作,例如網(wǎng)絡(luò)訪問(wèn)。

這里我們可以看到從最開始eventbus就通過(guò)反射將需要調(diào)用的函數(shù)加載到eventbus的類里保存下來(lái)了。不過(guò)這里也可以知道其實(shí)eventbus也只是通過(guò)handler來(lái)調(diào)用主界面的線程。秘密揭開了,自己也可以嘗試寫一套。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 先吐槽一下博客園的MarkDown編輯器,推出的時(shí)候還很高興博客園支持MarkDown了,試用了下發(fā)現(xiàn)支持不完善就...
    Ten_Minutes閱讀 661評(píng)論 0 2
  • Android 淺析EventBus (一) 使用 前言 Linus Benedict Torvalds : RT...
    CodePlayer_Jz閱讀 3,504評(píng)論 3 10
  • EventBus用法及源碼解析目錄介紹1.EventBus簡(jiǎn)介1.1 EventBus的三要素1.2 EventB...
    楊充211閱讀 2,043評(píng)論 0 4
  • 對(duì)于Android開發(fā)老司機(jī)來(lái)說(shuō)肯定不會(huì)陌生,它是一個(gè)基于觀察者模式的事件發(fā)布/訂閱框架,開發(fā)者可以通過(guò)極少的代碼...
    飛揚(yáng)小米閱讀 1,544評(píng)論 0 50
  • 1、給別人一個(gè)積極的期待,在發(fā)生你認(rèn)可行為的時(shí)候給予鼓勵(lì),在反方向時(shí)不需要給予批評(píng)。 如果你是權(quán)威,那么你的期待將...
    賭猩閱讀 312評(píng)論 0 0

友情鏈接更多精彩內(nèi)容