探索Android路由框架-ARouter之深挖源碼(二)

本文已獨(dú)家授權(quán) 郭霖 ( guolin_blog?) 公眾號(hào)發(fā)布!

在《?探索Android路由框架-ARouter(一)》中,主要介紹了ARouter框架的基本配置、多種跳轉(zhuǎn)方案崔慧、攔截器的使用等炒考。這一篇文章主要是對(duì)該框架的源碼進(jìn)行分析:????

ARouter是通過APT生成代碼在框架內(nèi)部進(jìn)行操作可缚,那么,項(xiàng)目編譯生成的文件位置在那里斋枢?

項(xiàng)目編譯生成的文件位置 ?

既然生成了這些源碼帘靡,我們就先隨便點(diǎn)點(diǎn)看看這些都是啥?

源碼 - 1
源碼 - 2
源碼 - 3
源碼 - 4

這里簡(jiǎn)簡(jiǎn)單單隨便截圖了APT生成的部分源碼瓤帚,是不是感覺跟上一篇文章使用到的代碼很多相似性吶~比如攔截器的優(yōu)先級(jí)是1描姚、跳轉(zhuǎn)匹配的路徑也是一樣的、跳轉(zhuǎn)傳遞的參數(shù)戈次、定義的組名等等轩勘。既然這么多一樣的那肯定是在內(nèi)部某部分進(jìn)行封裝使用,帶著這個(gè)問題我們開始逐步分析怯邪。

首先绊寻,我們從該框架使用到的注解開始分析(因?yàn)樽⒔馐鞘褂眠@個(gè)框架的起點(diǎn))

注解分析:

首先,我們知道要使用ARouter的首先需要在類的注釋上面寫上 @Route 這個(gè)注解悬秉,點(diǎn)進(jìn)源碼看看

Route注解

使用該注解標(biāo)注的類將被自動(dòng)添加至路由表中澄步。而且,ARouter 并非僅提供頁(yè)面(Activity)的路由功能和泌,還可以用來(lái)路由模塊想要暴露給其他模塊調(diào)用的接口村缸。也就是說(shuō)?@Route?不僅可用于 Activity 類,還可用于模塊對(duì)外接口的實(shí)現(xiàn)類允跑。那么具體它可以實(shí)現(xiàn)那些類型王凑?

從上面的源碼1搪柑、2聋丝、3、4截圖可以看到工碾,除了攔截器弱睦,里面通過實(shí)現(xiàn)接口重寫方法,方法里面都有一個(gè)RouteMeta渊额,那么我們點(diǎn)進(jìn)去RouteMeta這個(gè)類看看:

RouteMeta

其中紅色矩形中的類注釋一目了然况木、翻譯過來(lái):?它包含基本路由信息。

截圖中藍(lán)色的矩形旬迹,RouteType火惊,就是路由的類型。那么奔垦,這款路由框架的具體路由類型又有那些屹耐?帶著這個(gè)疑問,點(diǎn)進(jìn)RouteType?源碼看看椿猎。

RouterType

首先這是一個(gè)枚舉惶岭,這些就是框架可以具體使用到的路由類型

說(shuō)完Route 這個(gè)注解寿弱,我們?cè)趤?lái)看看@Interceptor?攔截器注解,

Interceptor 注解

哦按灶,我們發(fā)現(xiàn)攔截器的注解就2個(gè)方法症革,第一個(gè)是定義優(yōu)先級(jí)的,第二個(gè)就是攔截器的名字鸯旁。

接著噪矛,@Autowired 注解:

我們知道這個(gè)注解是界面跳轉(zhuǎn)時(shí)參數(shù)傳遞用的。Activity 中使用該注解標(biāo)志的變量羡亩,會(huì)在頁(yè)面被路由打開的時(shí)候自動(dòng)賦予傳遞的參數(shù)值摩疑。賦值的操作可以參考上面的源碼 - 4截圖。

初始化分析:

我們知道畏铆,ARouter框架使用的第一個(gè)步驟雷袋,是要先初始化,也就是調(diào)用:ARouter.init( Application.this );這個(gè)API辞居,那么楷怒,它的初始化究竟是做了那些東西?我們先點(diǎn)進(jìn)源碼看看:

ARouter.init()

那么瓦灶,實(shí)際上它調(diào)用的是綠色矩形內(nèi)的代碼鸠删,綠色矩形內(nèi)有兩個(gè)初始化,一個(gè)是?_ARouter.init(application)贼陶,還有一個(gè)是 _ARouter.afterInit( )刃泡。

我們首先點(diǎn)進(jìn) _ARouter . init()看看,

?_ARouter

A:紅色箭頭的是類注釋碉怔,翻譯過來(lái)就是:ARouter核心( 外觀模式 )

B:綠色箭頭代表的是一個(gè)線程池烘贴,對(duì)線程池概念不是很清楚的朋友可以點(diǎn)進(jìn)?必須要理清的Java線程池?先了解一下

C:藍(lán)色箭頭代表的是 這個(gè)_ARouter init()實(shí)際是調(diào)用的LogisticsCenter里面的init方法。

首先撮胧,什么是外觀模式桨踪?

簡(jiǎn)單點(diǎn)理解就是,通過創(chuàng)建一個(gè)統(tǒng)一的類芹啥,用來(lái)包裝子系統(tǒng)中一個(gè)或多個(gè)復(fù)雜的類锻离,客戶端可以通過調(diào)用外觀類的方法來(lái)調(diào)用內(nèi)部子系統(tǒng)中所有方法。大概意思就是這樣墓怀,想深入理解的話可以自行查閱資料汽纠。

其次,這個(gè)線程池做了什么功能傀履?

點(diǎn)進(jìn)去DefaultPoolExecutor這個(gè)類看看:

DefaultPoolExecutor

由源碼得知就是創(chuàng)建了一個(gè)數(shù)組阻塞隊(duì)列的線程池

最后虱朵,我們點(diǎn)進(jìn)LogisticsCenter,看看里面的init方法執(zhí)行了什么操作(因?yàn)樵创a太長(zhǎng),我就分別截圖)

LogisticsCenter - init (1)
LogisticsCenter - init (2)

通過LogisticsCenter - init(1)這幅源碼圖可以得知:如果是Debug模式卧秘,則執(zhí)行從if代碼塊呢袱; 如果是Release模式:通過PackageUtils.isNewVersion(context)判斷當(dāng)前應(yīng)用是否是第一次啟動(dòng)。接著翅敌,獲取arouter-compiler生成的文件羞福,然后將該文件,存儲(chǔ)在sp中蚯涮,下次啟動(dòng)應(yīng)用的時(shí)候治专,直接從sp緩存中讀取。

然后遭顶,在LogisticsCenter - init(2)紅色矩形的代碼塊可以得知:首先遍歷arouter-compiler生成的文件张峰,將他們按照類型分別存儲(chǔ)到Warehouse的對(duì)應(yīng)字段中。也就是棒旗,如果類名前綴符合文件拼接規(guī)則喘批,比如為com.alibaba.android.arouter.routes.ARouter$$Root的文件,就將其添加到具體的Warehouse里面的集合中铣揉。也就是對(duì)應(yīng)的這里

對(duì)應(yīng)的文件

那么饶深,這個(gè)文件拼接規(guī)則(也就是字符串拼接)是?

文件拼接規(guī)則

Warehouse又是什么逛拱?點(diǎn)進(jìn)源碼看看

Warehouse

其中敌厘,Warehouse的類注釋寫的非常好,翻譯過來(lái)就是:路由元數(shù)據(jù)和其他數(shù)據(jù)的存儲(chǔ)朽合。這個(gè)類本質(zhì)就是路由文件映射表俱两。里面提供了各種HashMap集合(Map不允許重復(fù)的key),去存儲(chǔ)SP存儲(chǔ)的值曹步。

我們?cè)倏纯催@里存儲(chǔ)攔截器的集合宪彩,UniqueKeyTreeMap,這個(gè)類是存儲(chǔ)攔截器的箭窜,我們看看它做了什么毯焕?

UniqueKeyTreeMap

原來(lái)衍腥,這個(gè)UniqueKeyTreeMap磺樱,就是繼承了TreeMap,哦婆咸,我們知道TreeMap是一種有序的集合(底層幫我們排序)所以數(shù)值越小竹捉,它就優(yōu)先添加攔截器。這樣也就解釋了為什么攔截器屬性值設(shè)置的越低尚骄,優(yōu)先級(jí)越高块差。

綜上,針對(duì)?_ARouter init( )?這個(gè)初始化的源碼我們可以得知以下:

A:初始化這一操作,表面上是_ARouter 憨闰,實(shí)則是LogisticsCenter 在幫我們管理邏輯

B:LogisticsCenter 內(nèi)部通過先存儲(chǔ)SP状蜗,然后遍歷、匹配(滿足條件則添加到具體的集合中鹉动,按照文件的前綴不同轧坎,將他們添加到路由映射表Warehouse的groupsIndex、interceptorsIndex泽示、providersIndex?中)

C:具體的路由清單是Warehouse 缸血,不僅保存實(shí)例,還給我們提供了緩存械筛。也就是說(shuō)?同一個(gè)目標(biāo)(RouteMeta捎泻、IProvider、IInterceptor)僅會(huì)在第一次使用的時(shí)候創(chuàng)建一次埋哟,然后緩存起來(lái)笆豁。后面都是直接使用的緩存。

初始化還有一個(gè)API赤赊,_ARouter.afterInit( ) ; 這個(gè)API在上面的截圖也有渔呵,那么這個(gè)API是用來(lái)干什么的?我們點(diǎn)進(jìn)去看看砍鸠。

_ARouter.afterInit

首先扩氢,它實(shí)例化了一個(gè)InterceptorService。這里的InterceptorService下面會(huì)說(shuō)爷辱。 這里的build方法录豺,最終返回的是一個(gè)Postcard對(duì)象:

build

從源碼可以得知,實(shí)際上它返回的卻是饭弓,_ARouter.getInstance().build(path)這個(gè)方法双饥,這個(gè)方法是一個(gè)方法重載(一般用的最多的就是這一個(gè),也就是默認(rèn)分組弟断,不進(jìn)行自定義分組)咏花,跟進(jìn)去看看(源碼很長(zhǎng),分為以下兩個(gè)截圖):

_ARouter.getInstance().build - 1
_ARouter.getInstance().build - 2

發(fā)現(xiàn)這里有一個(gè)PathReplaceService(也就是紅色矩形)阀趴,這個(gè)PathReplaceService又是什么昏翰,點(diǎn)進(jìn)去看看

PathReplaceService

這個(gè)類注釋翻譯過來(lái)就是:預(yù)處理路徑。這個(gè)接口是IProvider的子類

讓我們?cè)诳椿鼐G色箭頭刘急,其中棚菊,navigation(clazz)這種方式是屬于根據(jù)類型查找,而build(path)是根據(jù)名稱進(jìn)行查找叔汁。如果應(yīng)用中沒有實(shí)現(xiàn)PathReplaceService這個(gè)接口统求,則pService=null检碗。PathReplaceService可以對(duì)所有的路徑進(jìn)行預(yù)處理,然后返回一個(gè)新的值(返回一個(gè)新的String和Uri)码邻。綠色箭頭有一個(gè)extractGroup()方法折剃,點(diǎn)進(jìn)去看看:

extractGroup

extractGroup(path)這個(gè)方法,核心邏輯是紅色矩形內(nèi)的代碼像屋,這個(gè)方法主要是獲取分組名稱微驶。切割path字符串,默認(rèn)為path中第一部分為組名开睡。這就證明了如果我們不自定義分組因苹,默認(rèn)就是第一個(gè)分號(hào)的內(nèi)容。

分析完了build篇恒,我們?cè)诳纯磏avigation

navigation

繼續(xù)點(diǎn)進(jìn)源碼看看扶檐,發(fā)現(xiàn)進(jìn)入了_ARouter這個(gè)類里面的navigation方法:

navigation - 1
navigation - 2

其中,navigation - 1這幅圖中的紅色矩形代表的意思是胁艰,如果(兩個(gè))路徑?jīng)]寫對(duì)款筑,ARouter會(huì)Toast提示客戶端,路徑不對(duì)腾么。

navigation - 2這幅圖中的紅色矩形代表的是忽略攔截器奈梳。interceptorService實(shí)例化對(duì)象的時(shí)機(jī),是在_ARouter這類中的afterInit( )進(jìn)行實(shí)例化的解虱。這幅圖中的藍(lán)色矩形和箭頭的方法真實(shí)邏輯是調(diào)用了下圖的方法:

通過這段代碼我們可以得知:

1:如果navigation()不傳入Activity作為context攘须,則使用Application作為context

2:內(nèi)部使用了Intent來(lái)進(jìn)行傳遞

3:如果在跳轉(zhuǎn)時(shí),設(shè)置了flags殴泰,且沒有設(shè)置Activity作為context于宙,則下面的startActivity()方法會(huì)發(fā)生錯(cuò)誤,因?yàn)槿鄙貯ctivity的Task棧悍汛;

4:Fragment的判斷根據(jù)版本不同進(jìn)行了相應(yīng)的判斷

看到了這里捞魁,我們?cè)诳椿豱avigation - 2這幅圖。如果(兩個(gè))路徑匹配的話离咐,會(huì)執(zhí)行到截圖中的藍(lán)色矩形谱俭,點(diǎn)進(jìn)藍(lán)色矩形里面去看看(也就是?LogisticsCenter.completion( Postcard )):

LogisticsCenter.completion - 1
LogisticsCenter.completion - 2
LogisticsCenter.completion - 3

首先,根據(jù)path在Warehouse.routes映射表中查找對(duì)應(yīng)的RouteMeta宵蛀。但是昆著,第一次加載的時(shí)候,是沒有數(shù)據(jù)的糖埋。(而第一次加載是在LogisticsCenter.init()中宣吱,上面也說(shuō)了窃这。因此只有`Warehouse`的`groupsIndex瞳别、interceptorsIndex征候、providersIndex` 有數(shù)據(jù)),因此這個(gè)時(shí)候routeMeta=null祟敛。所以疤坝,這個(gè)時(shí)候會(huì)先從Warehouse.groupsIndex中取出類名前綴為com.alibaba.android.arouter.routes.ARouter$$Group$$group的文件;接著馆铁,將我們添加@Route注解的類映射到Warehouse.routes中跑揉;然后將已經(jīng)加載過的組從Warehouse.groupsIndex中移除,這樣也避免了重復(fù)添加進(jìn)Warehouse.routes埠巨;注意历谍,這個(gè)時(shí)候Warehouse.routes已經(jīng)有值了,所以重新調(diào)用本方法(completion(postcard))執(zhí)行了else代碼塊辣垒。

然后望侈,LogisticsCenter.completion - 2 圖中就是具體的設(shè)置屬性值;然后在?LogisticsCenter.completion - 3 圖中勋桶,對(duì)?IProvider的子類進(jìn)行初始化 provider.init(mContext); 初始化完畢以后脱衙,將IProvider的子類添加進(jìn)Warehouse.providers中;最后將IProvider的實(shí)現(xiàn)類保存在postcard中例驹,因此可以從postcard獲取IProvider的實(shí)例對(duì)象捐韩,其中,greenChannel()會(huì)忽略攔截器這個(gè)前面也說(shuō)了鹃锈。

小結(jié):

在LogisticsCenter.completion這個(gè)方法里面完成了對(duì)Warehouse.providers荤胁、Warehouse.routes的賦值。那么Warehouse.interceptors又是在哪里賦值的呢屎债?

IProvider有個(gè)子類接口寨蹋,InterceptorService(在文章的上面,也簡(jiǎn)單提到過這個(gè)接口)

InterceptorService

因此InterceptorServiceImpl也是IProvider的子類扔茅;在LogisticsCenter.completion()中已旧,有provider.init(context)的初始化 ;那么,我們看看InterceptorServiceImpl這個(gè)攔截器的實(shí)現(xiàn)類召娜,

InterceptorServiceImpl

在InterceptorServiceImpl這個(gè)類里面的紅色矩形中运褪,完成了具體攔截器的初始化以及將攔截器添加到Warehouse.interceptors映射表中。

寫到這里玖瘸,ARouter框架的源碼基本上就分析完畢了秸讹。可能你說(shuō)雅倒,這不是只是初始化嘛璃诀,是的這只是初始化,但是界面跳轉(zhuǎn)的源碼都涵蓋在上面了蔑匣,你可能不信劣欢,我們點(diǎn)擊跳轉(zhuǎn)的API去看看:

簡(jiǎn)單跳轉(zhuǎn)

點(diǎn)擊build棕诵,又跳進(jìn)熟悉的build里面來(lái)了:

build

同樣,點(diǎn)擊navigation凿将,也回到了上面的navigation中去了校套。

源碼基本上就寫到這里,內(nèi)容有點(diǎn)繞牧抵。個(gè)人建議自己對(duì)著源碼好好過一遍笛匙,這樣理解的效果可能會(huì)比較好。

如果這篇文章對(duì)您有開發(fā)or學(xué)習(xí)上的些許幫助犀变,希望各位看官留下寶貴的star妹孙,謝謝。

Ps:著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明作者, 商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)获枝,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處(開頭或結(jié)尾請(qǐng)?zhí)砑愚D(zhuǎn)載出處涕蜂,添加原文url地址),文章請(qǐng)勿濫用、開源項(xiàng)目?jī)H供學(xué)習(xí)交流映琳、也希望大家尊重筆者的勞動(dòng)成果,謝謝机隙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市萨西,隨后出現(xiàn)的幾起案子有鹿,更是在濱河造成了極大的恐慌,老刑警劉巖谎脯,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葱跋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡源梭,警方通過查閱死者的電腦和手機(jī)娱俺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)废麻,“玉大人荠卷,你說(shuō)我怎么就攤上這事≈蚶ⅲ” “怎么了油宜?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)怜姿。 經(jīng)常有香客問我慎冤,道長(zhǎng),這世上最難降的妖魔是什么沧卢? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任蚁堤,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘促煮。我一直安慰自己,他們只是感情好谦秧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布藤巢。 她就那樣靜靜地躺著搞莺,像睡著了一般息罗。 火紅的嫁衣襯著肌膚如雪掂咒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天迈喉,我揣著相機(jī)與錄音绍刮,去河邊找鬼。 笑死挨摸,一個(gè)胖子當(dāng)著我的面吹牛孩革,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播得运,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膝蜈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了熔掺?” 一聲冷哼從身側(cè)響起饱搏,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎置逻,沒想到半個(gè)月后推沸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡券坞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年鬓催,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恨锚。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宇驾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猴伶,到底是詐尸還是另有隱情飞苇,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布蜗顽,位于F島的核電站布卡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雇盖。R本人自食惡果不足惜忿等,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崔挖。 院中可真熱鬧贸街,春花似錦庵寞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逸尖,卻和暖如春古沥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娇跟。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工岩齿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苞俘。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓盹沈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親吃谣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乞封,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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

  • Arouter框架適合項(xiàng)目比較大,模塊多的時(shí)候岗憋,可以實(shí)現(xiàn)解耦肃晚,不需要知道跳轉(zhuǎn)的是哪個(gè)activity,只需要知道配...
    破曉11閱讀 3,466評(píng)論 0 2
  • 組件化 模塊化澜驮、組件化與插件化 在項(xiàng)目發(fā)展到一定程度陷揪,隨著人員的增多,代碼越來(lái)越臃腫杂穷,這時(shí)候就必須進(jìn)行模塊化的拆分...
    silentleaf閱讀 4,953評(píng)論 2 12
  • 本文章用于記錄筆者學(xué)習(xí) ARouter 源碼的過程悍缠,僅供參考,如有錯(cuò)誤之處還望悉心指出耐量,一起交流學(xué)習(xí)飞蚓。 ARout...
    DevLocke閱讀 13,959評(píng)論 6 52
  • 酒的危害,想必大家都知道吧廊蜒!飲次酒不算什么趴拧,可如果長(zhǎng)期飲酒,可能會(huì)引起肝脾腫大山叮、消化道炎癥甚至癌變呢著榴!可是老...
    海棠味的棒棒糖閱讀 384評(píng)論 0 0
  • 沒有什么不可能 你是我要的對(duì)的那個(gè)人 親愛的脑又,愿相伴左右,白首百年 不是需要所有人知道我愛你 只要我知道我懂我明白...
    心想榮閱讀 223評(píng)論 0 0