“Some Interesting Open Source Projects of Android”這個系列主要是對一些有意思的Android開源項目進行源碼分析,錯誤之處煩請指正~
由于EventBus3.0.0(后文統(tǒng)一簡寫成“EventBus”)較以前版本無論是使用性能還是使用方式都有較大差異,本文分析基于最新的3.0.0版本。(如果您還是用的以前的版本,建議升級到最新版本吧,耳目一新的EventBus~)
一、EventBus 簡介
伴隨業(yè)務發(fā)展,app愈發(fā)臃腫,引發(fā)了一系列問題:
不同組件間的通信也變得復雜起來,Activity、Fragments以及后臺線程間的通信造成app耦合度太高,非常不利于擴展。
代碼復雜。
EventBus基于解耦的通信方式被越來越多的開發(fā)者青睞,除了解決上述問題之外,還解決了復雜的依賴性問題和各種生命周期問題,加之EventBus的輕量級(~50k jar),成為廣為使用的組件間通信框架。
使用:
1.1 項目引入EventBus(Gradle)
打開對應模塊的build.gradle,在dependencies中加入EventBus依賴:
compile 'org.greenrobot:eventbus:3.0.0'
EventBus3.0最大的優(yōu)化就是通過EventBusAnnotationProcessor在編譯時生成索引,提升索引速度。所以,肯定得引入到項目中啦~
可以使用兩種方式通過編譯時注解框架生成索引,詳情可查看文檔。
(1)使用annotationProcessor
(2)使用android-apt
此時,再編譯一次,就會在項目中生成索引類。這樣就可以在初始化EventBus的時候應用生成的索引文件了。在項目初始化的時候,將生成的 *EventBusIndex 通過EventBusBuilder配置到EventBus中,一般在Application的onCreate方法中執(zhí)行該方法。
若項目開啟混淆,則需要在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 成功接收到事件
訂閱者需要將自身注冊到bus上 {org.greenrobot.eventbus.EventBus #register(Object)}
一旦注冊,訂閱者在從bus上注銷{org.greenrobot.eventbus.EventBus #unregister(Object)}之前,都能接收事件
1.3 注意事項
為避免反射帶來的耗時,EventBus通過編譯時注解,響應事件處理方法。事件處理方法有如下注意事項:
(1).方法必須加上{org.greenrobot.eventbus.Subscribe}注解
(2).方法必須為公共方法(public),無返回值(void)
(3).只有一個參數(shù)(發(fā)送的事件)
二、 源碼解析
源碼解析會按照使用流程分析源碼,然后對項目里有意思的設計進行分析。
2.1 注冊訂閱,接收事件
用戶(Activity,Fragment,View..)欲接收事件,需要實現(xiàn)接收對應事件的訂閱方法并將當前用戶注冊到EventBus。
2.1.1 注冊用戶
EventBus.builder().build()..register(Object subscriber)
若項目中對EventBus需要進行私有化定制,可以通過EventBusBuilder類定制相關配置。EventBusBuilder通過Builder模式實現(xiàn)自由擴展項目配置。
當然,若項目無個性化配置需求,全部采用默認設置,則注冊方法可以簡化為:EventBus.getDefault().register(Object subscriber)。
EventBus采用Double Check Lock(DCL)模式實現(xiàn)單例,保證全局只有唯一EventBus實例。代碼如下:
當用戶注冊EventBus后,會將所有訂閱的方法訂閱到EventBus中去。代碼如下:
注意:若注冊用戶及其父類沒有被@Subscribe注解的public方法,會拋出EventBusException方法。
2.1.2 訂閱方法
在已注冊的用戶中,加入對應訂閱方法。通過@Subscribe對方法進行注解(must have exactly 1 parameter, be public, non-static, and non-abstract),對方法名也取消了限制。
2.1.2.1 ThreadMode
ThreadMode提供四種線程模式:POSTING,MAIN,BACKGROUND,ASYNC,用以支持發(fā)送事件和處理接收事件線程獨立。
POSTING:在發(fā)送線程上處理接收事件,以保證最小開銷。使用過程中應避免在主線程發(fā)送事件以造成線程阻塞。
MAIN:在Android主線程(UI線程)處理接收事件。由于在主線程處理事件,所以在使用過程中應避免執(zhí)行耗時操作以造成線程阻塞。
BACKGROUND:后臺線程上處理接收事件,若在非主線程發(fā)送事件,則直接在當前線程處理接收。EventBus采用唯一的后臺線程,確保所有發(fā)送事件按順序交付。
ASYNC:單獨線程上處理接收事件,與后臺線程及主線程獨立。EventBus通過線程池有效的限制線程數(shù)量,并高效復用已執(zhí)行完畢異步任務。
不同線程模式下喚醒對應訂閱者方法代碼如下:
EventBus默認采用POSTING線程模式。
2.1.2.2 boolean sticky
sticky若為true,當訂閱者處于活躍狀態(tài)時,會交付最近的黏滯事件。在用戶注冊的時候,會先嘗試喚醒執(zhí)行一次訂閱的方法(已存儲的黏滯事件對象中有當前訂閱者方法,詳見2.2.2)。
EventBus默認sticky為false。
2.1.2.3 int priority
通過priority指定訂閱者優(yōu)先級以實現(xiàn)控制事件交付順序。
priority僅支持在相同ThreadMode下,高優(yōu)先級訂閱者會更早收到事件(被喚醒)。
EventBus默認所有訂閱者的priority均為0。
2.1.3 注銷已注冊用戶
EventBus.getDefault().unregister(this);
為節(jié)省開銷,養(yǎng)成良好的代碼習慣,需要在所有注冊(register)訂閱EventBus的類里在合適的時機注銷(unregister)訂閱。Activity及Fragment一般在對應生命周期函數(shù)內(nèi)采用對應方法,View中推薦在onAttachedToWindow()和onDetachedFromWindow()對應使用注冊及注銷方法(具體實現(xiàn)結(jié)合業(yè)務實現(xiàn)調(diào)整)。
注銷用戶代碼如下:
注銷當前用戶訂閱事件代碼如下:
2.1.4 小結(jié)
為保證程序開銷,切記在合適的時機對用戶進行注冊與注銷。結(jié)合業(yè)務,合理選擇線程模式以及優(yōu)先級。
至此,用戶訂閱流程全部完成。
2.2 發(fā)送事件
在業(yè)務邏輯中,將對應事件發(fā)送到EventBus,EventBus通過喚起對應可用的訂閱方法,完成全部模塊業(yè)務邏輯。
2.2.1 發(fā)送普通事件
EventBus.getDefault().post(Object event);
可發(fā)送的事件可以為一切對象。但在項目中一般為方便管理,推薦使用新建類作為單一事件使用。
發(fā)送事件后處理代碼如下:
對應線程模式下,喚起對應訂閱者方法見2.1.2.1前圖《不同線程模式下喚醒對應訂閱者方法》。
2.2.2 發(fā)送黏滯事件
EventBus.getDefault().postSticky(Object event);
同發(fā)送普通事件,可發(fā)送的事件可以為一切對象。但在項目中一般為方便管理,推薦使用新建類作為單一事件使用。
發(fā)送黏滯事件代碼如下:
存儲所有黏滯事件的stickyEvents會在所有用戶注冊的時候,一旦判斷該用戶有sticky=true的訂閱者方法,就會喚起對應訂閱者方法。
為節(jié)省開銷,應在相關接收事件的模塊及時回收掉當前發(fā)送的黏滯事件。
EventBus.getDefault().removeStickyEvent(Object event);
EventBus.getDefault().removeStickyEvent(Class eventType)
EventBus.getDefault().removeAllStickyEvents();//慎用
2.2.3 小結(jié)
發(fā)送普通事件后,EventBus喚醒對應可用的訂閱者方法即完成一次處理流程。
發(fā)送黏滯事件,為節(jié)省開銷,則需要在對應的時機回收掉該事件。
2.3 其他模塊介紹
2.3.1 編譯時注解框架(annotation processor)介紹
EventBus3.0中最大的升級之一是@Subscribe注解采用編譯時注解框架實現(xiàn)代碼插入。對應代碼插入實現(xiàn)在EventBusAnnotationProcessor模塊中。
在接入EventBus的模塊中,可以自由選擇是否通過注解生成訂閱者方法索引列表,從而實現(xiàn)索引加速。
在引用訂閱者索引加速后,編譯時會將所有訂閱方法存儲在一個list中,生成文件位于"module/build/genereated/source/apt/..."目錄下。
在EventBus接收到發(fā)送的事件后,會取出所有訂閱該事件的方法。若開啟索引加速后,則會直接從list中取出對應方法,否則通過反射查找對應方法,極大的影響性能。所以在EventBus3.0時應該開啟索引加速。
三、小結(jié)
項目引入EventBus,最大作用之一是為了各模塊解耦。通過靈活的線程模式,更大限度的滿足各種復雜的業(yè)務需求。
在使用的過程中,需要注意因使用不當造成的額外開銷造成資源浪費以及耗時索引等。下面對部分注意事項再次說明。
1)有注冊(register)則必有注銷(unregister)。
2)僅有@Subscribe注解訂閱方法的類里,才能在EventBus注冊當前用戶(register)。
3)所有采用@Subscribe注解的訂閱方法,必須是public,無返回值且只有一個參數(shù)(參數(shù)即為訂閱的事件)。對方法名已取消命名限制。
4)合理使用ThreadMode。尤其避免在主線程進行耗時操作造成主線程阻塞。
本文對EventBus源碼分析就到這了。在使用這些優(yōu)秀的開源庫的時候,花時間去弄清楚源碼的設計理論與實現(xiàn)方式,不僅讓我們對該開源庫有更強大的掌控能力,而且會對我們編程思想有裨益。
有任何錯誤部分,煩請指正,謝謝~
# 附: