本文已獨(dú)家授權(quán) 郭霖 ( guolin_blog?) 公眾號(hào)發(fā)布!
在《?探索Android路由框架-ARouter(一)》中,主要介紹了ARouter框架的基本配置、多種跳轉(zhuǎn)方案崔慧、攔截器的使用等炒考。這一篇文章主要是對(duì)該框架的源碼進(jìn)行分析:????
ARouter是通過APT生成代碼在框架內(nèi)部進(jìn)行操作可缚,那么,項(xiàng)目編譯生成的文件位置在那里斋枢?
既然生成了這些源碼帘靡,我們就先隨便點(diǎn)點(diǎn)看看這些都是啥?
這里簡(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)源碼看看
使用該注解標(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è)類看看:
其中紅色矩形中的類注釋一目了然况木、翻譯過來(lái):?它包含基本路由信息。
截圖中藍(lán)色的矩形旬迹,RouteType火惊,就是路由的類型。那么奔垦,這款路由框架的具體路由類型又有那些屹耐?帶著這個(gè)疑問,點(diǎn)進(jìn)RouteType?源碼看看椿猎。
首先這是一個(gè)枚舉惶岭,這些就是框架可以具體使用到的路由類型
說(shuō)完Route 這個(gè)注解寿弱,我們?cè)趤?lái)看看@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)源碼看看:
那么瓦灶,實(shí)際上它調(diào)用的是綠色矩形內(nèi)的代碼鸠删,綠色矩形內(nèi)有兩個(gè)初始化,一個(gè)是?_ARouter.init(application)贼陶,還有一個(gè)是 _ARouter.afterInit( )刃泡。
我們首先點(diǎn)進(jìn) _ARouter . init()看看,
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è)類看看:
由源碼得知就是創(chuàng)建了一個(gè)數(shù)組阻塞隊(duì)列的線程池
最后虱朵,我們點(diǎn)進(jìn)LogisticsCenter,看看里面的init方法執(zhí)行了什么操作(因?yàn)樵创a太長(zhǎng),我就分別截圖)
通過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)的這里
那么饶深,這個(gè)文件拼接規(guī)則(也就是字符串拼接)是?
Warehouse又是什么逛拱?點(diǎn)進(jìn)源碼看看
其中敌厘,Warehouse的類注釋寫的非常好,翻譯過來(lái)就是:路由元數(shù)據(jù)和其他數(shù)據(jù)的存儲(chǔ)朽合。這個(gè)類本質(zhì)就是路由文件映射表俱两。里面提供了各種HashMap集合(Map不允許重復(fù)的key),去存儲(chǔ)SP存儲(chǔ)的值曹步。
我們?cè)倏纯催@里存儲(chǔ)攔截器的集合宪彩,UniqueKeyTreeMap,這個(gè)類是存儲(chǔ)攔截器的箭窜,我們看看它做了什么毯焕?
原來(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)去看看砍鸠。
首先扩氢,它實(shí)例化了一個(gè)InterceptorService。這里的InterceptorService下面會(huì)說(shuō)爷辱。 這里的build方法录豺,最終返回的是一個(gè)Postcard對(duì)象:
從源碼可以得知,實(shí)際上它返回的卻是饭弓,_ARouter.getInstance().build(path)這個(gè)方法双饥,這個(gè)方法是一個(gè)方法重載(一般用的最多的就是這一個(gè),也就是默認(rèn)分組弟断,不進(jìn)行自定義分組)咏花,跟進(jìn)去看看(源碼很長(zhǎng),分為以下兩個(gè)截圖):
發(fā)現(xiàn)這里有一個(gè)PathReplaceService(也就是紅色矩形)阀趴,這個(gè)PathReplaceService又是什么昏翰,點(diǎn)進(jìn)去看看
這個(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(path)這個(gè)方法,核心邏輯是紅色矩形內(nèi)的代碼像屋,這個(gè)方法主要是獲取分組名稱微驶。切割path字符串,默認(rèn)為path中第一部分為組名开睡。這就證明了如果我們不自定義分組因苹,默認(rèn)就是第一個(gè)分號(hào)的內(nèi)容。
分析完了build篇恒,我們?cè)诳纯磏avigation
繼續(xù)點(diǎn)進(jìn)源碼看看扶檐,發(fā)現(xiàn)進(jìn)入了_ARouter這個(gè)類里面的navigation方法:
其中,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 )):
首先,根據(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è)接口)
因此InterceptorServiceImpl也是IProvider的子類扔茅;在LogisticsCenter.completion()中已旧,有provider.init(context)的初始化 ;那么,我們看看InterceptorServiceImpl這個(gè)攔截器的實(shí)現(xiàn)類召娜,
在InterceptorServiceImpl這個(gè)類里面的紅色矩形中运褪,完成了具體攔截器的初始化以及將攔截器添加到Warehouse.interceptors映射表中。
寫到這里玖瘸,ARouter框架的源碼基本上就分析完畢了秸讹。可能你說(shuō)雅倒,這不是只是初始化嘛璃诀,是的這只是初始化,但是界面跳轉(zhuǎn)的源碼都涵蓋在上面了蔑匣,你可能不信劣欢,我們點(diǎn)擊跳轉(zhuǎn)的API去看看:
點(diǎn)擊build棕诵,又跳進(jìn)熟悉的build里面來(lái)了:
同樣,點(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)成果,謝謝机隙。