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如果沒有找到對(duì)應(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)鍵就是在這里砰奕。把你所有的注冊(cè)信息已經(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對(duì)象
        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對(duì)版本有要求,確保Android Gradle插件版本是2.2以上形葬,我在測(cè)試的時(shí)候由于沒有去更新這些版本合呐,導(dǎo)致在Build的過程中太久(它去下載對(duì)應(yīng)的版本了),最后才去把所有版本包括AS版本笙以,Gradle淌实,buildToolsVersion全部更新了。

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末留美,一起剝皮案震驚了整個(gè)濱河市彰檬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谎砾,老刑警劉巖逢倍,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異景图,居然都是意外死亡较雕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門挚币,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亮蒋,“玉大人,你說我怎么就攤上這事妆毕∩骶粒” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵笛粘,是天一觀的道長(zhǎng)趁怔。 經(jīng)常有香客問我,道長(zhǎng)薪前,這世上最難降的妖魔是什么润努? 我笑而不...
    開封第一講書人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮示括,結(jié)果婚禮上铺浇,老公的妹妹穿的比我還像新娘。我一直安慰自己例诀,他們只是感情好随抠,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著繁涂,像睡著了一般拱她。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扔罪,一...
    開封第一講書人閱讀 52,821評(píng)論 1 314
  • 那天秉沼,我揣著相機(jī)與錄音,去河邊找鬼。 笑死唬复,一個(gè)胖子當(dāng)著我的面吹牛矗积,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播敞咧,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼棘捣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了休建?” 一聲冷哼從身側(cè)響起乍恐,我...
    開封第一講書人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎测砂,沒想到半個(gè)月后茵烈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砌些,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年呜投,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片存璃。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仑荐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纵东,到底是詐尸還是另有隱情释漆,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布篮迎,位于F島的核電站,受9級(jí)特大地震影響示姿,放射性物質(zhì)發(fā)生泄漏甜橱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一栈戳、第九天 我趴在偏房一處隱蔽的房頂上張望岂傲。 院中可真熱鬧,春花似錦子檀、人聲如沸镊掖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亩进。三九已至,卻和暖如春缩歪,著一層夾襖步出監(jiān)牢的瞬間归薛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留主籍,地道東北人习贫。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像千元,于是被迫代替她去往敵國和親苫昌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

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

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