EventBus源碼解析(三)Post方法和注解處理器

前兩篇文章講解了使用和register方法,本篇文章主要講解post方法以及注解處理器

EventBus源碼解析系列

EventBus源碼解析(一)關(guān)于用法和注解
EventBus源碼解析(二)register與unregister
EventBus源碼解析(三)Post方法和注解處理器

Post方法

從post開始

 public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

post里面主要進(jìn)行以下操作

  • ThreadLocal中獲取PostingThreadState,然后把事件Event添加都里面的隊(duì)列中postingState.eventQueue
    這里PostingThreadState這個(gè)類為
 final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<Object>();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }

用來保存一些些狀態(tài)

  • 判斷當(dāng)前事件是否發(fā)送,進(jìn)入到if中,設(shè)置是否是主線程以及posting=true,然后循環(huán)調(diào)用隊(duì)列postSingleEvent(eventQueue.remove(0), postingState);,使用完的事件則被移除隊(duì)列

  • 最后finally設(shè)置屬性為false

看下這個(gè)事件處理方法

 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        //①處理子類
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        //②結(jié)果判斷
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

這里也比較好理解,首先還是這個(gè)eventInheritance這個(gè)上篇有講過,是來處理含有子類的方法的。最終都是調(diào)用postSingleEventForEventType來處理事件,之后結(jié)果賦值給subscriptionFound如果沒有找到對應(yīng)的方法則消費(fèi)一個(gè)默認(rèn)事件NoSubscriberEvent

 private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //①根據(jù)事件類型eventType拿到方法集
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) { 
               //②保存當(dāng)前狀態(tài)到PostingState
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                    //③開始執(zhí)行
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

在這里通過subscriptions = subscriptionsByEventType.get(eventClass);拿到我們前面register解析到的方法集,之后每進(jìn)行一次處理事件都要設(shè)置下當(dāng)前PostingState的狀態(tài),而前面也有說到EventBus里面ThreadLocal的泛型是PostingState,這里每次設(shè)置當(dāng)前狀態(tài)主要用來獲取事件的執(zhí)行情況。

之后則調(diào)用postToSubscription來執(zhí)行事件

 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 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);
        }
    }

這個(gè)大家也比較熟悉了,跟前面一篇文章分析一樣,了解ThreadModel的使用后相信你自己也可以分析出來。

注解處理器

在第二篇文章中我們使用EventBus只是默認(rèn)的使用,并且最終還是使用反射來獲取類信息,這樣很耗性能,而且在3.0版本之前也是使用反射了。所以3.0之后多了個(gè)注解處理器的使用,它則是在編譯期將類信息,通過注解處理器,自動(dòng)生成索引,大大提高了EventBus的運(yùn)行效率。怎么使用呢,我們先看下配置說明。

打開App的build.gradle,在dependencies中添加最新的EventBus依賴:

compile 'org.greenrobot:eventbus:3.0.0'

然后在項(xiàng)目gradle的dependencies中引入apt編譯插件:

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

然后在App的build.gradle中應(yīng)用apt插件,并設(shè)置apt生成的索引的包名和類名:

apply plugin: 'com.neenbedankt.android-apt'
apt {
    arguments {
        eventBusIndex "com.yourpackage.MyEventBusIndex" //指定一個(gè)路徑下的,生成的類則是在這里
    }
}

接著在App的dependencies中引入EventBusAnnotationProcessor:

apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'

配置完成之后,我們暫且不編譯,先看下我們的訂閱情況
我在MainActivity.class中使用

  @Subscribe(threadMode = ThreadMode.MAIN , sticky = true , priority = 50)
    public void receiveEventString(String s){
        Log.e(TAG, "receiveEventString: " + s );
    }
    @Subscribe(threadMode = ThreadMode.BACKGROUND , sticky = false , priority = 100)
    public void receiveEventInt(Integer i){
        Log.e(TAG, "receiveEventInt: " + i );
    }

然后我們選擇編譯Build一下,看看生成的文件

目錄

自動(dòng)生成的目錄則是在這里。

我們看下這個(gè)生成類的信息

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
        //關(guān)鍵就是在這里。把你所有的注冊信息已經(jīng)在編譯時(shí)期獲取到,并且保存在Map中
        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("receiveEventString", String.class, ThreadMode.MAIN, 50, true),
            new SubscriberMethodInfo("receiveEventInt", Integer.class, ThreadMode.BACKGROUND, 100, false),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

可以看到在編譯器生成的這個(gè)處理器已經(jīng)獲取到類的所有注解信息封裝到SubscriberInfo中,然后存放在一個(gè)Map中,最后調(diào)用getSubscriberInfo就可以獲取到信息。
MyEventBusIndex是實(shí)現(xiàn)SubscriberInfoIndex接口

public interface SubscriberInfoIndex {
    SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);
}

好,我們回到上一篇文章獲取類方法那里。

 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
       //①獲取FindState對象
        FindState findState = prepareFindState();
        //初始化
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //②獲取訂閱者的信息,一開始為null,如果有使用注解處理器,則不為null
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //③通過反射來獲取方法信息,之后保存在findState
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        //④從findState中獲取到SubscriberMethod
        return getMethodsAndRelease(findState);
    }

之前則是在這里第二步②來判斷你有沒有使用注解處理器的。
②通過getSubscriberInfo(findState);根據(jù)當(dāng)前的findState來獲取訂閱者的信息。

 private SubscriberInfo getSubscriberInfo(FindState findState) {
        ...
        //判斷有沒有使用注解處理器,如果有使用,則在編譯器時(shí)候通過讀取@Subscribe()注解并解析保存到subscriberInfoIndexes中了。
        if (subscriberInfoIndexes != null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

關(guān)鍵就是這個(gè)subscriberInfoIndexes判斷,而這個(gè)subscriberInfoIndexes則在它構(gòu)造方法賦值的

  SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                           boolean ignoreGeneratedIndex) {
        this.subscriberInfoIndexes = subscriberInfoIndexes;
        ...
    }
 EventBus(EventBusBuilder builder) {
        ...
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
       ...
    }

最后則是通過Builder

  /** Adds an index generated by EventBus' annotation preprocessor. */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if(subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

在Builder這里只是提供了一個(gè)方法來讓你添加,而參數(shù)則是SubscriberInfoIndex,而剛剛我們那個(gè)自動(dòng)生成的處理器則是實(shí)現(xiàn)了這個(gè)接口。

所以只需要這樣設(shè)置便可以使用了

EventBus mEventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();

在這里則不能使用getDefalut來創(chuàng)建EventBus了。
EventBus默認(rèn)有一個(gè)單例,可以通過getDefault()獲取,也可以通過EventBus.builder()構(gòu)造自定義的EventBus,比如要應(yīng)用我們生成好的索引時(shí)。

AnnotationProcessor

前面我們說到注解處理器使用的是Apt,不過很遺憾,Apt作者已經(jīng)不維護(hù)這個(gè)插件了,現(xiàn)在則是提倡使用谷歌的AnnotationProcessor來代替apt。android-apt只支持javac編譯器,而annotationProcessor同時(shí)支持javac和jack編譯器。

如何配置呢


dependencies {
    ...
    compile 'org.greenrobot:eventbus:3.0.0'
    annotationProcessor  'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

只需要在dependencies這里添加annotationProcessor,而不使用apt了。

之后在defaultConfig配置添加

 defaultConfig {
        ...
        jackOptions {
            enabled true
        }
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'org.greenrobot.eventbusperf.MyEventBusIndex' ]
            }
        }
    }

就可以了。

不過這個(gè)AnnotationProcessor對版本有要求,確保Android Gradle插件版本是2.2以上,我在測試的時(shí)候由于沒有去更新這些版本,導(dǎo)致在Build的過程中太久(它去下載對應(yīng)的版本了),最后才去把所有版本包括AS版本,Gradle,buildToolsVersion全部更新了。

寫到最后也就可以發(fā)現(xiàn)EventBus的設(shè)計(jì)了。

EventBus采用了觀察者模式設(shè)計(jì),通過register(Object)方法來注冊當(dāng)前類監(jiān)聽,把所有注冊的類給保存起來,并且通過反射或者注解處理器拿到當(dāng)前類中的監(jiān)聽方法Method,最后在post發(fā)送事件的時(shí)候在內(nèi)部依次搜索每個(gè)注冊類,然后再根據(jù)post的參數(shù)類型(事件類型)進(jìn)行篩選對應(yīng)符合參數(shù)類型的方法,然后再根據(jù)ThreadMode設(shè)置的事件處理情況,選擇在哪個(gè)線程中處理調(diào)用反射方法,進(jìn)而接收到事件。

而在3.0之前的版本中是使用類似字符串匹配的方法,比如onEventMainThread這些,之后再使用反射獲取,而使用反射則是十分損耗性能的,
所以在3.0之后引入了注解的使用,而注解又引入了注解處理器AnnotationProcessor,能夠在編譯期就獲取到所有注冊類里面對應(yīng)的注解接收方法,然后在獲取的時(shí)候就不需要再去反射獲取類的方法,直接去使用,大大提升了性能。不過EventBus默認(rèn)是沒有使用注解處理器的,要自己去設(shè)置使用才有。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,592評(píng)論 25 707
  • 文章基于EventBus 3.0講解。首先對于EventBus的使用上,大多數(shù)人還是比較熟悉的。如果你還每次煩于使...
    Hohohong閱讀 2,313評(píng)論 0 6
  • 博文出處:EventBus源碼解析,歡迎大家關(guān)注我的博客,謝謝! 0001B 時(shí)近年末,但是也沒閑著。最近正好在看...
    俞其榮閱讀 1,304評(píng)論 1 16
  • 對于Android開發(fā)老司機(jī)來說肯定不會(huì)陌生,它是一個(gè)基于觀察者模式的事件發(fā)布/訂閱框架,開發(fā)者可以通過極少的代碼...
    飛揚(yáng)小米閱讀 1,483評(píng)論 0 50
  • 出現(xiàn)問題抓住核心,核心沒有問題就不會(huì)出現(xiàn)大問題,很多事情不容易把控,只要看清這點(diǎn)就好。 對于孩子填報(bào)志愿,這一周發(fā)...
    我就是那片云閱讀 387評(píng)論 0 0