EventBus源碼解析

知識(shí)點(diǎn)匯總:

一:EventBus框架概述?

二:EventBus的注冊(cè)實(shí)現(xiàn)原理?

三:EventBus的事件分發(fā)實(shí)現(xiàn)原理

四:項(xiàng)目擴(kuò)展知識(shí)點(diǎn)?

五:擴(kuò)展閱讀


一:EventBus框架概述

描述:EventBus是一個(gè)Android事件發(fā)布/訂閱框架,通過(guò)解耦發(fā)布者和訂閱者簡(jiǎn)化Android事件傳遞,使項(xiàng)目接入和使用都十分方便瓦侮,下面是官方的項(xiàng)目概述:

? ? ? ?從上面的官方解析圖中浴鸿,我們注意到項(xiàng)目的整體設(shè)計(jì)是基于訂閱者模式進(jìn)行設(shè)計(jì)的,在訂閱者模式中幔托,主要涉及到的核心模塊主要有兩部分:事件的訂閱、解除訂閱處理 和 事件的分發(fā)處理。

1蟀架、事件的訂閱、解除訂閱處理:該模塊在訂閱者模式中榆骚,主要是用來(lái)分類并保存項(xiàng)目中所有訂閱者的信息辜窑,就好比有一千個(gè)讀者訂閱了報(bào)社的不同類型的報(bào)紙,報(bào)社需要對(duì)不同的訂閱者進(jìn)行分類并保存相關(guān)的訂閱者信息寨躁,當(dāng)讀者解除了相關(guān)的訂閱時(shí)穆碎,報(bào)社如何解除并刪除相關(guān)分類的訂閱信息,使下次發(fā)布報(bào)紙時(shí)不再發(fā)送給相關(guān)用戶职恳。?

2所禀、事件的分發(fā)處理:該模塊在訂閱者模式中,主要是針對(duì)不同的訂閱者放钦,發(fā)送相關(guān)的訂閱信息給到相應(yīng)的訂閱者中色徘,就好比當(dāng)前報(bào)社有一千個(gè)訂閱者,報(bào)社需要根據(jù)不同的訂閱者操禀,快遞不同的訂閱報(bào)紙給到不同的訂閱者手上褂策。 ????

? ? ? 上面通過(guò)現(xiàn)實(shí)生活中的例子,我們可以更加了解EventBus項(xiàng)目使用到的核心設(shè)計(jì)思想颓屑,從而讓我們更好的了解具體的項(xiàng)目代碼實(shí)現(xiàn)斤寂,下面我們就一起了解一下Eventbus是如何實(shí)現(xiàn)相關(guān)模塊的。


二:EventBus的注冊(cè)實(shí)現(xiàn)原理

下面我們先大體了解一下EventBus是如何實(shí)現(xiàn)訂閱模塊的設(shè)計(jì)的:

備注:在該項(xiàng)目中揪惦,我們可以看到不同的數(shù)據(jù)結(jié)構(gòu)存放著不同的數(shù)據(jù)遍搞,其中比較重要的點(diǎn)在于我們需要知道最終發(fā)送訂閱消息是根據(jù)訂閱的函數(shù)參數(shù)作為分類發(fā)送訂閱消息的,也就是說(shuō)器腋,不同的訂閱者的分組是根據(jù)訂閱的函數(shù)的參數(shù)類型進(jìn)行分組的溪猿,而不是根據(jù)訂閱函數(shù)所在的類進(jìn)行分組的,然后在進(jìn)行分組時(shí)纫塌,還需要根據(jù)不同訂閱函數(shù)的ThreadMode诊县、Sticky、Priority屬性進(jìn)行同一個(gè)分類的不同處理措左。

訂閱相關(guān)函數(shù)代碼:

解析:傳入的參數(shù)是訂閱函數(shù)所在的類對(duì)象依痊,通過(guò)類對(duì)象,獲取到相關(guān)的類的類型媳荒,查找到相關(guān)類類型所有的訂閱函數(shù)數(shù)據(jù)抗悍,并通過(guò)對(duì)查找到的訂閱函數(shù)驹饺,分別針對(duì)不同的訂閱函數(shù)的參數(shù)和屬性(ThreadMode、Sticky缴渊、Priority)赏壹,實(shí)現(xiàn)不同的處理,并最終根據(jù)函數(shù)的參數(shù)實(shí)現(xiàn)對(duì)訂閱者的分類并保存衔沼。

我們看一下如何通過(guò)訂閱函數(shù)的類對(duì)象蝌借,查找到相關(guān)的訂閱函數(shù)的,下面是實(shí)現(xiàn)代碼:

解析:首先會(huì)去緩存查找是否有相關(guān)類的訂閱函數(shù)數(shù)據(jù)指蚁,如果沒(méi)有菩佑,下面提供的兩種方式去獲取訂閱函數(shù)信息,分別是:

1凝化、通過(guò)反射遍歷相關(guān)對(duì)象中使用@subscribe注解的函數(shù)稍坯,從而獲取訂閱函數(shù)相關(guān)數(shù)據(jù)。

2搓劫、通過(guò)APT注解處理器動(dòng)態(tài)生成的訂閱函數(shù)的數(shù)據(jù)集合瞧哟,從而獲取訂閱函數(shù)相關(guān)數(shù)據(jù)。

? ? ? ? ?這里為什么要設(shè)計(jì)兩種實(shí)現(xiàn)方式枪向,主要是考慮到反射的實(shí)現(xiàn)方式效率會(huì)比較低勤揩,所以才會(huì)出現(xiàn)第二種實(shí)現(xiàn)方式,有興趣的朋友可以了解一下相關(guān)的具體代碼實(shí)現(xiàn)秘蛔。

下面就是訂閱事件相關(guān)代碼:

解析:通過(guò)遍歷函數(shù)subscribe函數(shù)(參數(shù)為:訂閱函數(shù)的類和訂閱函數(shù))陨亡,把項(xiàng)目中所有的訂閱函數(shù)封裝在對(duì)象Subscription對(duì)象中,再根據(jù)訂閱的事件類型對(duì)不同的訂閱函數(shù)進(jìn)行分組存放深员,保存在Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType哈希表中负蠕,函數(shù)中還包含對(duì)訂閱函數(shù)優(yōu)先級(jí)、粘性等屬性的處理辨液,后續(xù)事件分發(fā)就通過(guò)subscriptionsByEventType表虐急,根據(jù)事件類型找到所有訂閱函數(shù)列表,并根據(jù)訂閱函數(shù)的配置屬性滔迈,依次分發(fā)事件。

三:EventBus的事件分發(fā)實(shí)現(xiàn)原理

備注:對(duì)于訂閱事件的分發(fā)處理被辑,由于在訂閱模塊已經(jīng)根據(jù)事件類型把不同的訂閱函數(shù)進(jìn)行的分類存儲(chǔ)了燎悍,在事件分發(fā)模塊主要就是針對(duì)不同的訂閱函數(shù) 和 相關(guān)訂閱函數(shù)的不同屬性的設(shè)置(ThreadMode、Priority)盼理,實(shí)現(xiàn)對(duì)不同訂閱函數(shù)進(jìn)行分發(fā)處理的過(guò)程谈山,由于訂閱函數(shù)的ThreadMode線程模式不同,不同的事件類型函數(shù)需要在不同的線程中的事件隊(duì)列中排隊(duì)(PendingPostQueue)處理宏怔。

下面我們就看看事件分發(fā)相關(guān)相關(guān)函數(shù)實(shí)現(xiàn)代碼:

解析:上面代碼通過(guò)獲得發(fā)送分發(fā)事件的線程的分發(fā)事件隊(duì)列奏路,并將當(dāng)前需要在不用線程分發(fā)的事件通過(guò)遍歷按順序依次執(zhí)行畴椰,依次執(zhí)行通過(guò)調(diào)用函數(shù)postSingleEvent實(shí)現(xiàn),最后鸽粉,不同的分發(fā)事件集合由于不同的訂閱函數(shù)的屬性配置不同斜脂,會(huì)在不同的線程隊(duì)列中排隊(duì),并依次執(zhí)行触机,核心函數(shù)實(shí)現(xiàn)在postToSubscription函數(shù)中帚戳。

postToSubscription函數(shù)的實(shí)現(xiàn)代碼:

最終執(zhí)行函數(shù):

解析:從上面代碼可以看出,由于不同的訂閱函數(shù)的ThreadMode不同儡首,代碼中針對(duì)不同的屬性分別做了不同的處理片任,處理的實(shí)現(xiàn)剛好對(duì)應(yīng)五種不同的線程參數(shù)類型,并最終通過(guò)反射函數(shù)invoke實(shí)現(xiàn)對(duì)相關(guān)訂閱函數(shù)的執(zhí)行蔬胯。


四:項(xiàng)目擴(kuò)展知識(shí)點(diǎn)與答疑

4.1对供、項(xiàng)目中涉及到的數(shù)據(jù)結(jié)構(gòu)有哪些,他們?cè)谑裁磮?chǎng)景使用比較合適氛濒?

1产场、CopyOnWriteArrayList

2、PendingPostQueue

1泼橘、CopyOnWriteArrayList數(shù)據(jù)結(jié)構(gòu):

? ? ? 項(xiàng)目中的使用:Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType涝动,CopyOnWriteArrayList的關(guān)鍵點(diǎn):

1、內(nèi)部持有一個(gè)ReentrantLock lock = new ReentrantLock()

2炬灭、底層是用volatile transient聲明的數(shù)組 array

3醋粟、讀寫分離,寫時(shí)復(fù)制出一個(gè)新的數(shù)組重归,完成插入米愿、修改或者移除操作后將新數(shù)組賦值給array

? ? ? ?在項(xiàng)目中通過(guò)事件分發(fā)對(duì)象類型,獲取所有訂閱的函數(shù)對(duì)象鼻吮,通過(guò)讀寫分離育苟,完成相關(guān)訂閱對(duì)象在不同線程執(zhí)行時(shí)數(shù)據(jù)的一致性,解決相關(guān)同步問(wèn)題椎木。

2违柏、PendingPostQueue數(shù)據(jù)結(jié)構(gòu):

解析:該數(shù)據(jù)結(jié)構(gòu)是個(gè)隊(duì)列的數(shù)據(jù)結(jié)構(gòu),分別有參數(shù)隊(duì)頭和隊(duì)尾參數(shù)香椎,提供了入隊(duì)和出隊(duì)等函數(shù)的實(shí)現(xiàn)漱竖,在項(xiàng)目中通過(guò)該數(shù)據(jù)結(jié)構(gòu),可以同步的處理分發(fā)事件畜伐。


4.2馍惹、ThreadLocal的數(shù)據(jù)結(jié)構(gòu)與使用場(chǎng)景?

項(xiàng)目的使用代碼:

????private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {

????????@Override

????????protected PostingThreadState initialValue() {

????????????return new PostingThreadState();

????????}

????};

ThreadLocal解析:多線程訪問(wèn)同一個(gè)共享變量的時(shí)候容易出現(xiàn)并發(fā)問(wèn)題,特別是多個(gè)線程對(duì)一個(gè)變量進(jìn)行寫入的時(shí)候万矾,為了保證線程安全悼吱,一般使用者在訪問(wèn)共享變量的時(shí)候需要進(jìn)行額外的同步措施才能保證線程安全性偷仿。ThreadLocal是除了加鎖這種同步方式之外的一種保證一種規(guī)避多線程訪問(wèn)出現(xiàn)線程不安全的方法圃泡,當(dāng)我們?cè)趧?chuàng)建一個(gè)變量后,如果每個(gè)線程對(duì)其進(jìn)行訪問(wèn)的時(shí)候訪問(wèn)的都是線程自己的變量這樣就不會(huì)存在線程不安全問(wèn)題秸歧。

  ThreadLocal是JDK包提供的们颜,它提供線程本地變量吕朵,如果創(chuàng)建ThreadLocal變量,那么訪問(wèn)這個(gè)變量的每個(gè)線程都會(huì)有這個(gè)變量的一個(gè)副本窥突,在實(shí)際多線程操作的時(shí)候努溃,操作的是自己本地內(nèi)存中的變量,從而規(guī)避了線程安全問(wèn)題阻问。

原理概述:Thread類中有兩個(gè)變量threadLocals和inheritableThreadLocals梧税,二者都是ThreadLocal內(nèi)部類ThreadLocalMap類型的變量,我們通過(guò)查看內(nèi)部?jī)?nèi)ThreadLocalMap可以發(fā)現(xiàn)實(shí)際上它類似于一個(gè)HashMap称近。在默認(rèn)情況下第队,每個(gè)線程中的這兩個(gè)變量都為null,只有當(dāng)線程第一次調(diào)用ThreadLocal的set或者get方法的時(shí)候才會(huì)創(chuàng)建他們刨秆。除此之外凳谦,和我所想的不同的是,每個(gè)線程的本地變量不是存放在ThreadLocal實(shí)例中衡未,而是放在調(diào)用線程的ThreadLocals變量里面(前面也說(shuō)過(guò)尸执,該變量是Thread類的變量)。也就是說(shuō)缓醋,ThreadLocal類型的本地變量是存放在具體的線程空間上如失,其本身相當(dāng)于一個(gè)裝載本地變量的工具殼,通過(guò)set方法將value添加到調(diào)用線程的threadLocals中送粱,當(dāng)調(diào)用線程調(diào)用get方法時(shí)候能夠從它的threadLocals中取出變量褪贵。如果調(diào)用線程一直不終止,那么這個(gè)本地變量將會(huì)一直存放在他的threadLocals中抗俄,所以不使用本地變量的時(shí)候需要調(diào)用remove方法將threadLocals中刪除不用的本地變量脆丁。

? ?通過(guò)上面的對(duì)LocalThread的了解和項(xiàng)目中的使用,我們了解到动雹,項(xiàng)目中需要通過(guò)LocalThread存儲(chǔ)不同線程相關(guān)的分發(fā)事件隊(duì)列偎快,從而可以實(shí)現(xiàn)不同分發(fā)事件的線程可以同步的高效的執(zhí)行相關(guān)的分發(fā)事件。


4.3洽胶、APT注解處理器的調(diào)試流程?

步驟一:在項(xiàng)目gradle.properties文件中,添加配置代碼:

org.gradle.daemon=true

org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

-Dorg.gradle.debug=true


步驟二:在運(yùn)行的主項(xiàng)目中姊氓,添加APT注解處理器的module模塊丐怯,配置如下:(dependencies下)

annotationProcessor project(':eventbus-annotation-processor')


步驟三:點(diǎn)開(kāi)頂部菜單的Run的子菜單Edit Configurations,配置遠(yuǎn)程調(diào)試項(xiàng)目:


步驟四:把自己配置的APT設(shè)置調(diào)試模式翔横,并給相關(guān)Processor類添加斷點(diǎn)

備注:有時(shí)候點(diǎn)擊APT的調(diào)試模式時(shí)读跷,會(huì)提示無(wú)法連接5005端口,導(dǎo)致無(wú)法進(jìn)入調(diào)試模式禾唁,這是可以關(guān)閉暫用相關(guān)端口的應(yīng)用或重新啟動(dòng)AS解決問(wèn)題效览。

4.4、如何通過(guò)APT動(dòng)態(tài)生成代碼:MyEventBusIndex類荡短?

解析:下面我們一起看看注解處理器的核心代碼丐枉,下面截取出注解處理器回調(diào)函數(shù)process的相關(guān)核心代碼。

解析:代碼中主要有三個(gè)邏輯處理函數(shù):

1掘托、通過(guò)注解處理器相關(guān)參數(shù):Set<? extends TypeElement> annotations, RoundEnvironment env瘦锹,收集到項(xiàng)目中的所有訂閱函數(shù),并存放在數(shù)據(jù)結(jié)構(gòu)methodsByClass中闪盔。

2弯院、通過(guò)對(duì)訂閱函數(shù)的修飾符、參數(shù)列表等過(guò)濾條件泪掀,匯總出不符合訂閱條件的訂閱函數(shù)听绳。

3、通過(guò)processingEnv和BufferedWriter實(shí)現(xiàn)代碼的動(dòng)態(tài)生成异赫。


4.5椅挣、AsyncPoster和BackgroundPoster的區(qū)別是什么?

解析:通過(guò)源碼了解到祝辣,AsyncPoster是異步實(shí)現(xiàn)的事件分發(fā)贴妻,隊(duì)列中的事件分發(fā)沒(méi)有相關(guān)的同步處理限制,不需要等待前面的分發(fā)事件處理完成蝙斜,就可以執(zhí)行后面的分發(fā)事件名惩,而B(niǎo)ackgroundPoster則有相關(guān)的限制。


下面本人繪制出Eventbus項(xiàng)目的綜合解析圖:

五:擴(kuò)展閱讀

1孕荠、https://www.cnblogs.com/all88/p/5338412.html(EventBus3.0源碼解析)

2娩鹉、https://blog.csdn.net/tomatomas/article/details/53998585/(如何調(diào)試編譯時(shí)注解處理器AnnotationProcessor)

3、https://www.cnblogs.com/fsmly/p/11020641.html#_label0(Java中的ThreadLocal詳解)

4稚伍、https://juejin.cn/post/6844904201290514446(APT Android代碼怎么調(diào)試)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弯予,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子个曙,更是在濱河造成了極大的恐慌锈嫩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異呼寸,居然都是意外死亡艳汽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門对雪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)河狐,“玉大人,你說(shuō)我怎么就攤上這事瑟捣〔鲆眨” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵迈套,是天一觀的道長(zhǎng)捐祠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)交汤,這世上最難降的妖魔是什么雏赦? 我笑而不...
    開(kāi)封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮芙扎,結(jié)果婚禮上星岗,老公的妹妹穿的比我還像新娘。我一直安慰自己戒洼,他們只是感情好俏橘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著圈浇,像睡著了一般寥掐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磷蜀,一...
    開(kāi)封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天召耘,我揣著相機(jī)與錄音,去河邊找鬼褐隆。 笑死污它,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庶弃。 我是一名探鬼主播衫贬,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歇攻!你這毒婦竟也來(lái)了固惯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤缴守,失蹤者是張志新(化名)和其女友劉穎葬毫,沒(méi)想到半個(gè)月后镇辉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡供常,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年摊聋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栈暇。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖箍镜,靈堂內(nèi)的尸體忽然破棺而出源祈,到底是詐尸還是另有隱情,我是刑警寧澤色迂,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布香缺,位于F島的核電站,受9級(jí)特大地震影響歇僧,放射性物質(zhì)發(fā)生泄漏图张。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一诈悍、第九天 我趴在偏房一處隱蔽的房頂上張望祸轮。 院中可真熱鬧,春花似錦侥钳、人聲如沸适袜。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)苦酱。三九已至,卻和暖如春给猾,著一層夾襖步出監(jiān)牢的瞬間疫萤,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工敢伸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扯饶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓详拙,卻偏偏與公主長(zhǎng)得像帝际,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饶辙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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