功能
EventBus 是一個 Android 事件發(fā)布/訂閱框架居暖,通過解耦發(fā)布者和訂閱者簡化 Android 事件傳遞契邀。事件傳遞既可用于 Android 四大組件間通訊奈辰,也可以用戶異步線程和主線程間通訊等等忙上。傳統(tǒng)的事件傳遞方式包括:Handler藕漱、BroadCastReceiver、Interface 回調(diào)臀稚,相比之下 EventBus 的優(yōu)點(diǎn)是代碼簡潔市袖,使用簡單,并將事件發(fā)布和訂閱充分解耦烁涌。
使用
這是GitHub,EventBus的使用介紹酒觅,用起來還是很簡單的撮执。
類圖
從上面的類圖可以看出,都是圍繞類EventBus 通過組合來建立關(guān)聯(lián)舷丹。
可以看到優(yōu)秀的框架設(shè)計(jì)抒钱,多用組合,少用繼承颜凯。
源碼分析
EventBus.getDefault()谋币;
獲取Event Bus的實(shí)例:
使用單例模式來創(chuàng)建出Event Bus對象,采用了雙重驗(yàn)證 以保證線程安全症概。蕾额,看看Event Bus的構(gòu)造函數(shù)。
在構(gòu)造函數(shù)里進(jìn)行了一系列的初始化工作彼城,主要通過類EventBusBuilder 使用Build的模式來初始化EventBus的一些配置诅蝶。
還有幾個重要的數(shù)據(jù)結(jié)構(gòu)集合的初始化:
subscriptionsByEventType
訂閱該事件的所有訂閱者 退个。是一個以 key:訂閱的事件 value:訂閱這個事件的所有訂閱者。因此當(dāng)post的時候會遍歷這個Map相應(yīng)對應(yīng)事件的List调炬。
typesBySubscriber
訂閱者中的所有訂閱事件 语盈。是一個以key:訂閱者 value:訂閱者中的所有訂閱事件。用于后續(xù)的取消訂閱缰泡。
三個Poster類
Poster類是用來處理sticky事件刀荒。
register(this)
委托SubscriberMethodFinder
類,查找出訂閱者中的所有訂閱事件方法棘钞。進(jìn)入findSubscriberMethods
方法看看:
該方法的處理流程大概是這樣的:
這里我們只分析 通過findUsingReflection
方法缠借,通過反射獲取訂閱者中的所有訂閱事件方法。進(jìn)去看看這個方法:
查找到的訂閱事件會在臨時的類FindState中做校驗(yàn)和保存武翎,為什么會提到這個臨時類呢烈炭?這里有一個很好的設(shè)計(jì),使用了“對象池”的概念來創(chuàng)建FindState對象宝恶,和線程池同一個概念符隙, 這樣可以使對象FindState復(fù)用,防止創(chuàng)建過多的對象垫毙,增加內(nèi)存開銷霹疫。
去看看如何復(fù)用FindState對象的,進(jìn)入上圖prepareFindState
方法:
在112中 可以看到 會從對象池FIND_STATE_POOL
取出FindState 而不是每次都創(chuàng)建對象综芥。
回到 findUsingReflection
方法當(dāng)查找完所有的訂閱事件方法丽蝎,調(diào)用getMethodsAndRelease
對FindState進(jìn)行回收復(fù)用:
還是回到 findUsingReflection
方法,調(diào)用了findUsingReflectionInSingleClass
方法來進(jìn)行查找訂閱事件方法膀藐,進(jìn)去看看是怎么查找的:
- 在154行 通過反射 獲取訂閱者(this)的所有方法屠阻。
- 在160 行遍歷這些方法。
- 在164行 判斷是否帶@Subscribe 注解的方法 是否只有一個參數(shù)额各,如果否 就會拋出異常
- 在165行判斷方法是否帶@Subscribe 注解 国觉,如果是,通過FindState對象進(jìn)行校驗(yàn)和保存虾啦。
其實(shí)很簡單 就是通過反射來查找到訂閱者的所有方法麻诀,查找出帶@Subscribe注解的方法保存起來。
回到register
方法傲醉,查找完所有的訂閱者中的所有訂閱事件方法蝇闭,之后遍歷列表,進(jìn)入subscribe
方法:
- 在146行 獲取訂閱方法的訂閱類型(就是帶@Subscribe 注解的方法的第一個參數(shù))硬毕。
- 封裝
Subscription
對象呻引,保存到subscriptionsByEventType
數(shù)據(jù)集合中。 - 在159行吐咳,根據(jù)優(yōu)先級 把封裝
Subscription
對象苞七,保存到typesBySubscriber
數(shù)據(jù)集合中藐守。 - 在174行 如果是
sticky
事件,立即post sticky
事件到當(dāng)前訂閱者蹂风。
整個register的流程大概就是這樣:
post 事件
最后通過post方法 來發(fā)送事件卢厂,進(jìn)入post方法
首先從currentPostingThreadState 獲取 PostingThreadState 對象
currentPostingThreadState 是個ThreadLocal對象
ThreadLocal使得各線程能夠保持各自獨(dú)立的一個對象。
把要post的事件加入PostingThreadState 的eventQueue隊(duì)列中惠啄,循環(huán)取出事件慎恒。
進(jìn)入postSingleEvent方法
- 在365行,判斷是否觸發(fā)訂閱該事件的父類撵渡,接口的方法融柬。
- 調(diào)用postSingleEventForEventType 方法分發(fā)事件。
進(jìn)入postSingleEventForEventType方法
- 在389行趋距,從subscriptionsByEventType數(shù)據(jù)集合中取出該訂閱事件的所有訂閱者粒氧。
- 在392行,分發(fā)所有的訂閱者节腐,通過調(diào)用postToSubscription方法外盯。
進(jìn)入postToSubscription方法
根據(jù)不同的threadMode 在不同的線程中處理,最終都會調(diào)用invokeSubscriber方法翼雀,把事件invoke到訂閱者的方法中饱苟。
ThreadMode 共有四類:
- PostThread:默認(rèn)的 ThreadMode,表示在執(zhí)行 Post 操作的線程直接調(diào)用訂閱者的事件響應(yīng)方法狼渊,不論該線程是否為主線程(UI 線程)箱熬。當(dāng)該線程為主線程時,響應(yīng)方法中不能有耗時操作狈邑,否則有卡主線程的風(fēng)險(xiǎn)城须。適用場景:對于是否在主線程執(zhí)行無要求,但若 Post 線程為主線程米苹,不能耗時的操作酿傍;
- MainThread:在主線程中執(zhí)行響應(yīng)方法。如果發(fā)布線程就是主線程驱入,則直接調(diào)用訂閱者的事件響應(yīng)方法,否則通過主線程的 Handler 發(fā)送消息在主線程中處理——調(diào)用訂閱者的事件響應(yīng)函數(shù)氯析。顯然亏较,MainThread
類的方法也不能有耗時操作,以避免卡主線程掩缓。適用場景:必須在主線程執(zhí)行的操作雪情; - BackgroundThread:在后臺線程中執(zhí)行響應(yīng)方法。如果發(fā)布線程不是主線程你辣,則直接調(diào)用訂閱者的事件響應(yīng)函數(shù)巡通,否則啟動唯一的后臺線程去處理尘执。由于后臺線程是唯一的,當(dāng)事件超過一個的時候宴凉,它們會被放在隊(duì)列中依次執(zhí)行誊锭,因此該類響應(yīng)方法雖然沒有PostThread類和MainThread類方法對性能敏感,但最好不要有重度耗時的操作或太頻繁的輕度耗時操作弥锄,以造成其他操作等待丧靡。適用場景:操作輕微耗時且不會過于頻繁,即一般的耗時操作都可以放在這里籽暇;
- Async:不論發(fā)布線程是否為主線程温治,都使用一個空閑線程來處理。和BackgroundThread不同的是戒悠,Async類的所有線程是相互獨(dú)立的熬荆,因此不會出現(xiàn)卡線程的問題。適用場景:長耗時操作绸狐,例如網(wǎng)絡(luò)訪問卤恳。
unregister
從 typesBySubscriber 數(shù)據(jù)集合中取出該訂閱者的所有訂閱方法,remove所有訂閱者的訂閱方法六孵,防止內(nèi)存泄漏纬黎。
整個post流程:
最后
EventBus 屬于一個比較容易理解的開源庫,項(xiàng)目整體的框架設(shè)計(jì)采用了觀察者模式劫窒,但不論從使用方式和實(shí)現(xiàn)方式上都是非常值得我們學(xué)習(xí)的開源項(xiàng)目本今。比如,對象池的設(shè)計(jì)主巍,三大Poster類的設(shè)計(jì)冠息,多用組合,少用繼承孕索,緩存的設(shè)計(jì)等都值得我們借鑒和使用逛艰。
END。