“Some Interesting Open Source Projects of Android”這個(gè)系列主要是對(duì)一些有意思的Android開源項(xiàng)目進(jìn)行源碼分析,錯(cuò)誤之處煩請(qǐng)指正~
由于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
(2)使用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)目配置。
當(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í)例。代碼如下:
當(dāng)用戶注冊(cè)EventBus后,會(huì)將所有訂閱的方法訂閱到EventBus中去侨糟。代碼如下:
注意:若注冊(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ì)方法名也取消了限制悲幅。
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)訂閱者方法代碼如下:
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)整)土浸。
注銷用戶代碼如下:
注銷當(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ā)送事件后處理代碼如下:
對(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ā)送黏滯事件代碼如下:
存儲(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)該開啟索引加速处坪。
三、小結(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)指正,謝謝~
# 附: