EventBus3(3.0.0)源碼解析

Some Interesting Open Source Projects of Android”這個系列主要是對一些有意思的Android開源項目進行源碼分析,錯誤之處煩請指正~

EventBus is a publish/subscribe event bus optimized for 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

annotationProcessor

(2)使用android-apt

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)自由擴展項目配置。

EventBusBuilder 配置清單

當然,若項目無個性化配置需求,全部采用默認設置,則注冊方法可以簡化為:EventBus.getDefault().register(Object subscriber)。

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

Double Check Lock(DCL) 單例模式

當用戶注冊EventBus后,會將所有訂閱的方法訂閱到EventBus中去。代碼如下:

注冊用戶

注意:若注冊用戶及其父類沒有被@Subscribe注解的public方法,會拋出EventBusException方法。

2.1.2 訂閱方法

在已注冊的用戶中,加入對應訂閱方法。通過@Subscribe對方法進行注解(must have exactly 1 parameter, be public, non-static, and non-abstract),對方法名也取消了限制。

@Subscribe

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ā)送事件后處理代碼如下:

post single event

對應線程模式下,喚起對應訂閱者方法見2.1.2.1前圖《不同線程模式下喚醒對應訂閱者方法》。

2.2.2 發(fā)送黏滯事件

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

同發(fā)送普通事件,可發(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時應該開啟索引加速。

SubscriberMethodFinder#findUsingInfo

三、小結(jié)

項目引入EventBus,最大作用之一是為了各模塊解耦。通過靈活的線程模式,更大限度的滿足各種復雜的業(yè)務需求。

在使用的過程中,需要注意因使用不當造成的額外開銷造成資源浪費以及耗時索引等。下面對部分注意事項再次說明。

1)有注冊(register)則必有注銷(unregister)。

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

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

4)合理使用ThreadMode。尤其避免在主線程進行耗時操作造成主線程阻塞。


本文對EventBus源碼分析就到這了。在使用這些優(yōu)秀的開源庫的時候,花時間去弄清楚源碼的設計理論與實現(xiàn)方式,不僅讓我們對該開源庫有更強大的掌控能力,而且會對我們編程思想有裨益。

有任何錯誤部分,煩請指正,謝謝~

# 附:

EventBus GitHub 項目地址

EventBus 官方文檔地址

Android編譯時注解框架系列

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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