EventBus 事件線程切換原理

今天學(xué)習(xí)了一下EventBus 的源碼,將其在接收事件時(shí)指定線程執(zhí)行的大致原理梳理了一下,提煉整理,作為學(xué)習(xí)總結(jié),也方便日后查閱。

正文

EventBus 可以在不同的線程發(fā)送事件、并在指定類型的線程接收和處理事件。在不同線程情況下,發(fā)送、接收并處理事件大致有4種情況:
A:在主線程發(fā)送,在子線程發(fā)送;B:在主線程執(zhí)行訂閱方法,在子線程執(zhí)行訂閱方法;AB兩兩搭配會(huì)有四種組合,也就是在不同線程情況下,發(fā)送、接收并處理事件的4種情況。
主要的問題其實(shí)只有兩個(gè),其一:如何判斷當(dāng)前發(fā)送事件的線程是否是主線程;其二:如何在接收事件時(shí)指定線程并執(zhí)行;
一個(gè)一個(gè)來看。

1.如何判斷是否在主線程發(fā)送

EventBus在初始化的時(shí)候會(huì)初始化一個(gè)MainThreadSupport對(duì)象,它會(huì)去獲取主線程的Looper對(duì)象并存起來。(當(dāng)前最新版本如果不是Android環(huán)境MainThreadSupport會(huì)為空,非Android環(huán)境也就無需關(guān)注是否是UI主線程的問題了)
在發(fā)送消息的時(shí)候,EventBus會(huì)取出當(dāng)前線程的Looper對(duì)象對(duì)象與主線程Looper對(duì)象做比較,如果相同,說明是在主線程發(fā)送消息,如果不同,說明是在子線程發(fā)送消息。以下是MainThreadSupport的代碼:

public interface MainThreadSupport {

    boolean isMainThread();

    Poster createPoster(EventBus eventBus);

    class AndroidHandlerMainThreadSupport implements MainThreadSupport {

        private final Looper looper;

        public AndroidHandlerMainThreadSupport(Looper looper) {
            this.looper = looper;
        }

        @Override
        public boolean isMainThread() {
            return looper == Looper.myLooper();
        }

        @Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);
        }
    }
}
2.怎么在指定的線程執(zhí)行訂閱者的方法

在找到訂閱者之后,判斷不同線程情況下執(zhí)行訂閱方法的邏輯基本都在postToSubscription()方法里面:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

以上代碼中,如何判斷是否是主線程上面已經(jīng)說過了。
invokeSubscriber()這個(gè)方法其實(shí)就是拿到訂閱者的信息,直接執(zhí)行訂閱方法了(通過反射獲取)。
subscription對(duì)象中有一個(gè)SubscriberMethod對(duì)象,而SubscriberMethod這個(gè)對(duì)象基本上包含了訂閱者的執(zhí)行線程、訂閱方法、是否粘性事件、優(yōu)先級(jí)等等信息。如下:

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;
    //省略若干代碼...
}

所以,從postToSubscription()方法可以看出,當(dāng)threadMode是POSTING時(shí),直接在當(dāng)前線程執(zhí)行,不做判斷,也就是從哪個(gè)線程發(fā)送,就從哪個(gè)線程執(zhí)行訂閱方法;
我們這里主要來看threadMode為MAIN和BACKGROUND的情況:

在主線程執(zhí)行

當(dāng)threadMode為MAIN時(shí),如果在主線程發(fā)送,直接在當(dāng)前線程執(zhí)行,沒有問題。如果不在主線程發(fā)送,會(huì)有一個(gè)mainThreadPoster將包含訂閱者信息的對(duì)象加入隊(duì)列。這個(gè)mainThreadPoster其實(shí)是Handler的子類,它利用Handler的消息機(jī)制,發(fā)送消息并在主線程接收消息,獲取到訂閱者的信息后在主線程處理事件,從而實(shí)現(xiàn)在子線程發(fā)送消息,在主線程處理事件。
這里直接利用Hadler的現(xiàn)成機(jī)制,可謂簡(jiǎn)明高效。

在子線程執(zhí)行

當(dāng)threadMode為BACKGROUND時(shí),如果不在主線程發(fā)送,直接執(zhí)行,沒有問題。如果在主線程發(fā)送,這里有一個(gè)backgroundPoster將包含訂閱者信息的對(duì)象加入隊(duì)列。BackgroundPoster其實(shí)是Runnable的子類,在自己的run方法中不斷從隊(duì)列中取出訂閱者對(duì)象,執(zhí)行訂閱方法。EventBus維護(hù)了一個(gè)線程池,BackgroundPoster會(huì)將自己丟到線程池中,執(zhí)行自己的run方法,從而實(shí)現(xiàn)在在主線程發(fā)送事件,在子線程中執(zhí)行訂閱方法。

以上,就是EventBus切換執(zhí)行線程的主要流程。
其實(shí)并不難。
末尾附上一篇講解EventBus原理的簡(jiǎn)練博文:戳這里

最后編輯于
?著作權(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)容

  • 項(xiàng)目到了一定階段會(huì)出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動(dòng)性越來越大,代碼維護(hù)與測(cè)試回歸流程越來越繁瑣。這個(gè)...
    fdacc6a1e764閱讀 3,330評(píng)論 0 6
  • EventBus地址:https://github.com/greenrobot/EventBus 一、event...
    君莫看閱讀 2,066評(píng)論 2 11
  • 先吐槽一下博客園的MarkDown編輯器,推出的時(shí)候還很高興博客園支持MarkDown了,試用了下發(fā)現(xiàn)支持不完善就...
    Ten_Minutes閱讀 651評(píng)論 0 2
  • 剛剛在“成長(zhǎng)匯”做婷婷的開場(chǎng)介紹, 看見念頭: 就文字吧,文字介紹婷婷可以的。 沒問題。 問自己這是你想要的嗎? ...
    尚靈心閱讀 485評(píng)論 1 5
  • 道理都是講給別人的,而你是用來愛的。
    失寵大雞排閱讀 161評(píng)論 0 0

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