EventBus3(3.0.0)源碼解析

Some Interesting Open Source Projects of Android”這個(gè)系列主要是對(duì)一些有意思的Android開源項(xiàng)目進(jìn)行源碼分析,錯(cuò)誤之處煩請(qǐng)指正~

EventBus is a publish/subscribe event bus optimized for Android.


由于EventBus3.0.0(后文統(tǒng)一簡(jiǎn)寫成“EventBus”)較以前版本無論是使用性能還是使用方式都有較大差異,本文分析基于最新的3.0.0版本伟端。(如果您還是用的以前的版本,建議升級(jí)到最新版本吧澡绩,耳目一新的EventBus~)


一、EventBus 簡(jiǎn)介

伴隨業(yè)務(wù)發(fā)展俺附,app愈發(fā)臃腫肥卡,引發(fā)了一系列問題:

不同組件間的通信也變得復(fù)雜起來,Activity事镣、Fragments以及后臺(tái)線程間的通信造成app耦合度太高步鉴,非常不利于擴(kuò)展。

代碼復(fù)雜。

EventBus基于解耦的通信方式被越來越多的開發(fā)者青睞氛琢,除了解決上述問題之外喊递,還解決了復(fù)雜的依賴性問題和各種生命周期問題,加之EventBus的輕量級(jí)(~50k jar),成為廣為使用的組件間通信框架阳似。

使用:

1.1 項(xiàng)目引入EventBus(Gradle)

打開對(duì)應(yīng)模塊的build.gradle骚勘,在dependencies中加入EventBus依賴:

compile 'org.greenrobot:eventbus:3.0.0'

EventBus3.0最大的優(yōu)化就是通過EventBusAnnotationProcessor在編譯時(shí)生成索引,提升索引速度撮奏。所以俏讹,肯定得引入到項(xiàng)目中啦~

可以使用兩種方式通過編譯時(shí)注解框架生成索引,詳情可查看文檔畜吊。

(1)使用annotationProcessor

annotationProcessor

(2)使用android-apt

android-apt

此時(shí)泽疆,再編譯一次,就會(huì)在項(xiàng)目中生成索引類玲献。這樣就可以在初始化EventBus的時(shí)候應(yīng)用生成的索引文件了殉疼。在項(xiàng)目初始化的時(shí)候,將生成的 *EventBusIndex 通過EventBusBuilder配置到EventBus中青自,一般在Application的onCreate方法中執(zhí)行該方法株依。

若項(xiàng)目開啟混淆,則需要在proguard中加入如下代碼:

-keepattributes*Annotation*

-keepclassmembersclass**{

@org.greenrobot.eventbus.Subscribe;

}

-keepenumorg.greenrobot.eventbus.ThreadMode{*;}

# Only required if you use AsyncExecutor

-keepclassmembersclass*extendsorg.greenrobot.eventbus.util.ThrowableFailureEvent{

(java.lang.Throwable);

}

1.2 成功接收到事件

訂閱者需要將自身注冊(cè)到bus上 {org.greenrobot.eventbus.EventBus #register(Object)}

一旦注冊(cè)延窜,訂閱者在從bus上注銷{org.greenrobot.eventbus.EventBus #unregister(Object)}之前,都能接收事件

1.3 注意事項(xiàng)

為避免反射帶來的耗時(shí)抹锄,EventBus通過編譯時(shí)注解逆瑞,響應(yīng)事件處理方法。事件處理方法有如下注意事項(xiàng):

(1).方法必須加上{org.greenrobot.eventbus.Subscribe}注解

(2).方法必須為公共方法(public),無返回值(void)

(3).只有一個(gè)參數(shù)(發(fā)送的事件)


二伙单、 源碼解析

源碼解析會(huì)按照使用流程分析源碼获高,然后對(duì)項(xiàng)目里有意思的設(shè)計(jì)進(jìn)行分析。

2.1 注冊(cè)訂閱吻育,接收事件

用戶(Activity,Fragment,View..)欲接收事件念秧,需要實(shí)現(xiàn)接收對(duì)應(yīng)事件的訂閱方法并將當(dāng)前用戶注冊(cè)到EventBus。

2.1.1 注冊(cè)用戶

EventBus.builder().build()..register(Object subscriber)

若項(xiàng)目中對(duì)EventBus需要進(jìn)行私有化定制布疼,可以通過EventBusBuilder類定制相關(guān)配置摊趾。EventBusBuilder通過Builder模式實(shí)現(xiàn)自由擴(kuò)展項(xiàng)目配置。

EventBusBuilder 配置清單

當(dāng)然游两,若項(xiàng)目無個(gè)性化配置需求砾层,全部采用默認(rèn)設(shè)置,則注冊(cè)方法可以簡(jiǎn)化為:EventBus.getDefault().register(Object subscriber)贱案。

EventBus采用Double Check Lock(DCL)模式實(shí)現(xiàn)單例肛炮,保證全局只有唯一EventBus實(shí)例。代碼如下:

Double Check Lock(DCL) 單例模式

當(dāng)用戶注冊(cè)EventBus后,會(huì)將所有訂閱的方法訂閱到EventBus中去侨糟。代碼如下:

注冊(cè)用戶

注意:若注冊(cè)用戶及其父類沒有被@Subscribe注解的public方法碍扔,會(huì)拋出EventBusException方法。

2.1.2 訂閱方法

在已注冊(cè)的用戶中秕重,加入對(duì)應(yīng)訂閱方法不同。通過@Subscribe對(duì)方法進(jìn)行注解(must have exactly 1 parameter, be public, non-static, and non-abstract),對(duì)方法名也取消了限制悲幅。

@Subscribe

2.1.2.1 ThreadMode

ThreadMode提供四種線程模式:POSTING,MAIN,BACKGROUND,ASYNC套鹅,用以支持發(fā)送事件和處理接收事件線程獨(dú)立。

POSTING:在發(fā)送線程上處理接收事件汰具,以保證最小開銷卓鹿。使用過程中應(yīng)避免在主線程發(fā)送事件以造成線程阻塞。

MAIN:在Android主線程(UI線程)處理接收事件留荔。由于在主線程處理事件吟孙,所以在使用過程中應(yīng)避免執(zhí)行耗時(shí)操作以造成線程阻塞。

BACKGROUND:后臺(tái)線程上處理接收事件聚蝶,若在非主線程發(fā)送事件杰妓,則直接在當(dāng)前線程處理接收。EventBus采用唯一的后臺(tái)線程碘勉,確保所有發(fā)送事件按順序交付巷挥。

ASYNC:?jiǎn)为?dú)線程上處理接收事件,與后臺(tái)線程及主線程獨(dú)立验靡。EventBus通過線程池有效的限制線程數(shù)量倍宾,并高效復(fù)用已執(zhí)行完畢異步任務(wù)。

不同線程模式下喚醒對(duì)應(yīng)訂閱者方法代碼如下:

不同線程模式下喚醒對(duì)應(yīng)訂閱者方法

EventBus默認(rèn)采用POSTING線程模式胜嗓。

2.1.2.2 boolean sticky

sticky若為true高职,當(dāng)訂閱者處于活躍狀態(tài)時(shí),會(huì)交付最近的黏滯事件辞州。在用戶注冊(cè)的時(shí)候怔锌,會(huì)先嘗試喚醒執(zhí)行一次訂閱的方法(已存儲(chǔ)的黏滯事件對(duì)象中有當(dāng)前訂閱者方法,詳見2.2.2)变过。

EventBus默認(rèn)sticky為false埃元。

2.1.2.3 int priority

通過priority指定訂閱者優(yōu)先級(jí)以實(shí)現(xiàn)控制事件交付順序。

priority僅支持在相同ThreadMode下牵啦,高優(yōu)先級(jí)訂閱者會(huì)更早收到事件(被喚醒)亚情。

EventBus默認(rèn)所有訂閱者的priority均為0。

2.1.3 注銷已注冊(cè)用戶

EventBus.getDefault().unregister(this);

為節(jié)省開銷哈雏,養(yǎng)成良好的代碼習(xí)慣楞件,需要在所有注冊(cè)(register)訂閱EventBus的類里在合適的時(shí)機(jī)注銷(unregister)訂閱衫生。Activity及Fragment一般在對(duì)應(yīng)生命周期函數(shù)內(nèi)采用對(duì)應(yīng)方法,View中推薦在onAttachedToWindow()和onDetachedFromWindow()對(duì)應(yīng)使用注冊(cè)及注銷方法(具體實(shí)現(xiàn)結(jié)合業(yè)務(wù)實(shí)現(xiàn)調(diào)整)土浸。

注銷用戶代碼如下:

注銷已注冊(cè)用戶

注銷當(dāng)前用戶訂閱事件代碼如下:

注銷當(dāng)前用戶訂閱事件

2.1.4 小結(jié)

為保證程序開銷罪针,切記在合適的時(shí)機(jī)對(duì)用戶進(jìn)行注冊(cè)與注銷。結(jié)合業(yè)務(wù)黄伊,合理選擇線程模式以及優(yōu)先級(jí)泪酱。

至此,用戶訂閱流程全部完成还最。

2.2 發(fā)送事件

在業(yè)務(wù)邏輯中砌左,將對(duì)應(yīng)事件發(fā)送到EventBus粘衬,EventBus通過喚起對(duì)應(yīng)可用的訂閱方法,完成全部模塊業(yè)務(wù)邏輯。

2.2.1 發(fā)送普通事件

EventBus.getDefault().post(Object event);

可發(fā)送的事件可以為一切對(duì)象胶果。但在項(xiàng)目中一般為方便管理蒲稳,推薦使用新建類作為單一事件使用胖翰。

發(fā)送事件后處理代碼如下:

post single event

對(duì)應(yīng)線程模式下荧缘,喚起對(duì)應(yīng)訂閱者方法見2.1.2.1前圖《不同線程模式下喚醒對(duì)應(yīng)訂閱者方法》。

2.2.2 發(fā)送黏滯事件

EventBus.getDefault().postSticky(Object event);

同發(fā)送普通事件枣氧,可發(fā)送的事件可以為一切對(duì)象溢十。但在項(xiàng)目中一般為方便管理,推薦使用新建類作為單一事件使用达吞。

發(fā)送黏滯事件代碼如下:

發(fā)送黏滯事件

存儲(chǔ)所有黏滯事件的stickyEvents會(huì)在所有用戶注冊(cè)的時(shí)候张弛,一旦判斷該用戶有sticky=true的訂閱者方法,就會(huì)喚起對(duì)應(yīng)訂閱者方法酪劫。

為節(jié)省開銷乌庶,應(yīng)在相關(guān)接收事件的模塊及時(shí)回收掉當(dāng)前發(fā)送的黏滯事件。

EventBus.getDefault().removeStickyEvent(Object event);

EventBus.getDefault().removeStickyEvent(Class eventType)

EventBus.getDefault().removeAllStickyEvents();//慎用

2.2.3 小結(jié)

發(fā)送普通事件后契耿,EventBus喚醒對(duì)應(yīng)可用的訂閱者方法即完成一次處理流程。

發(fā)送黏滯事件螃征,為節(jié)省開銷搪桂,則需要在對(duì)應(yīng)的時(shí)機(jī)回收掉該事件。

2.3 其他模塊介紹

2.3.1 編譯時(shí)注解框架(annotation processor)介紹

EventBus3.0中最大的升級(jí)之一是@Subscribe注解采用編譯時(shí)注解框架實(shí)現(xiàn)代碼插入盯滚。對(duì)應(yīng)代碼插入實(shí)現(xiàn)在EventBusAnnotationProcessor模塊中踢械。

在接入EventBus的模塊中,可以自由選擇是否通過注解生成訂閱者方法索引列表魄藕,從而實(shí)現(xiàn)索引加速内列。

在引用訂閱者索引加速后,編譯時(shí)會(huì)將所有訂閱方法存儲(chǔ)在一個(gè)list中背率,生成文件位于"module/build/genereated/source/apt/..."目錄下话瞧。

在EventBus接收到發(fā)送的事件后嫩与,會(huì)取出所有訂閱該事件的方法。若開啟索引加速后交排,則會(huì)直接從list中取出對(duì)應(yīng)方法划滋,否則通過反射查找對(duì)應(yīng)方法,極大的影響性能埃篓。所以在EventBus3.0時(shí)應(yīng)該開啟索引加速处坪。

SubscriberMethodFinder#findUsingInfo

三、小結(jié)

項(xiàng)目引入EventBus架专,最大作用之一是為了各模塊解耦同窘。通過靈活的線程模式,更大限度的滿足各種復(fù)雜的業(yè)務(wù)需求部脚。

在使用的過程中想邦,需要注意因使用不當(dāng)造成的額外開銷造成資源浪費(fèi)以及耗時(shí)索引等。下面對(duì)部分注意事項(xiàng)再次說明睛低。

1)有注冊(cè)(register)則必有注銷(unregister)案狠。

2)僅有@Subscribe注解訂閱方法的類里,才能在EventBus注冊(cè)當(dāng)前用戶(register)钱雷。

3)所有采用@Subscribe注解的訂閱方法骂铁,必須是public,無返回值且只有一個(gè)參數(shù)(參數(shù)即為訂閱的事件)罩抗。對(duì)方法名已取消命名限制拉庵。

4)合理使用ThreadMode。尤其避免在主線程進(jìn)行耗時(shí)操作造成主線程阻塞套蒂。


本文對(duì)EventBus源碼分析就到這了钞支。在使用這些優(yōu)秀的開源庫的時(shí)候操刀,花時(shí)間去弄清楚源碼的設(shè)計(jì)理論與實(shí)現(xiàn)方式烁挟,不僅讓我們對(duì)該開源庫有更強(qiáng)大的掌控能力,而且會(huì)對(duì)我們編程思想有裨益骨坑。

有任何錯(cuò)誤部分撼嗓,煩請(qǐng)指正,謝謝~

# 附:

EventBus GitHub 項(xiàng)目地址

EventBus 官方文檔地址

Android編譯時(shí)注解框架系列

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欢唾,一起剝皮案震驚了整個(gè)濱河市斑芜,隨后出現(xiàn)的幾起案子盈包,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件排嫌,死亡現(xiàn)場(chǎng)離奇詭異怖糊,居然都是意外死亡遣钳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門撞叽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辫狼,“玉大人见秤,你說我怎么就攤上這事。” “怎么了浙于?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挟纱。 經(jīng)常有香客問我羞酗,道長,這世上最難降的妖魔是什么紊服? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任檀轨,我火速辦了婚禮,結(jié)果婚禮上欺嗤,老公的妹妹穿的比我還像新娘参萄。我一直安慰自己,他們只是感情好剂府,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布拧揽。 她就那樣靜靜地躺著,像睡著了一般腺占。 火紅的嫁衣襯著肌膚如雪淤袜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天衰伯,我揣著相機(jī)與錄音铡羡,去河邊找鬼。 笑死意鲸,一個(gè)胖子當(dāng)著我的面吹牛烦周,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怎顾,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼读慎,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了槐雾?” 一聲冷哼從身側(cè)響起夭委,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎募强,沒想到半個(gè)月后株灸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崇摄,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年慌烧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逐抑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屹蚊,死狀恐怖厕氨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淑翼,我是刑警寧澤腐巢,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站玄括,受9級(jí)特大地震影響冯丙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遭京,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一胃惜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哪雕,春花似錦船殉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至堡僻,卻和暖如春糠惫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钉疫。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工硼讽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人牲阁。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓固阁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親城菊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子备燃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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

  • 前言 在上一篇文章:EventBus 3.0初探: 入門使用及其使用 完全解析中,筆者為大家介紹了EventBus...
    丶藍(lán)天白云夢(mèng)閱讀 15,843評(píng)論 21 128
  • 簡(jiǎn)介 我們知道凌唬,Android應(yīng)用主要是由4大組件構(gòu)成赚爵。當(dāng)我們進(jìn)行組件間通訊時(shí),由于位于不同的組件,通信方式相對(duì)麻...
    Whyn閱讀 537評(píng)論 0 1
  • 我每周會(huì)寫一篇源代碼分析的文章,以后也可能會(huì)有其他主題.如果你喜歡我寫的文章的話,歡迎關(guān)注我的新浪微博@達(dá)達(dá)達(dá)達(dá)s...
    SkyKai閱讀 24,927評(píng)論 23 184
  • 原文鏈接:http://blog.csdn.net/u012810020/article/details/7005...
    tinyjoy閱讀 546評(píng)論 1 5
  • 對(duì)于Android開發(fā)老司機(jī)來說肯定不會(huì)陌生冀膝,它是一個(gè)基于觀察者模式的事件發(fā)布/訂閱框架,開發(fā)者可以通過極少的代碼...
    飛揚(yáng)小米閱讀 1,475評(píng)論 0 50