本文已獨(dú)家授權(quán) 郭霖 ( guolin_blog?) 公眾號發(fā)布!
在《?探索Android路由框架-ARouter(一)》中,主要介紹了ARouter框架的基本配置、多種跳轉(zhuǎn)方案、攔截器的使用等。這一篇文章主要是對該框架的源碼進(jìn)行分析:????
ARouter是通過APT生成代碼在框架內(nèi)部進(jìn)行操作,那么,項(xiàng)目編譯生成的文件位置在那里?

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




這里簡簡單單隨便截圖了APT生成的部分源碼,是不是感覺跟上一篇文章使用到的代碼很多相似性吶~比如攔截器的優(yōu)先級是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 并非僅提供頁面(Activity)的路由功能,還可以用來路由模塊想要暴露給其他模塊調(diào)用的接口。也就是說?@Route?不僅可用于 Activity 類,還可用于模塊對外接口的實(shí)現(xiàn)類。那么具體它可以實(shí)現(xiàn)那些類型?
從上面的源碼1、2、3、4截圖可以看到,除了攔截器,里面通過實(shí)現(xiàn)接口重寫方法,方法里面都有一個(gè)RouteMeta,那么我們點(diǎn)進(jìn)去RouteMeta這個(gè)類看看:

其中紅色矩形中的類注釋一目了然、翻譯過來:?它包含基本路由信息。
截圖中藍(lán)色的矩形,RouteType,就是路由的類型。那么,這款路由框架的具體路由類型又有那些?帶著這個(gè)疑問,點(diǎn)進(jìn)RouteType?源碼看看。

首先這是一個(gè)枚舉,這些就是框架可以具體使用到的路由類型
說完Route 這個(gè)注解,我們在來看看@Interceptor?攔截器注解,

哦,我們發(fā)現(xiàn)攔截器的注解就2個(gè)方法,第一個(gè)是定義優(yōu)先級的,第二個(gè)就是攔截器的名字。
接著,@Autowired 注解:

我們知道這個(gè)注解是界面跳轉(zhuǎn)時(shí)參數(shù)傳遞用的。Activity 中使用該注解標(biāo)志的變量,會(huì)在頁面被路由打開的時(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:紅色箭頭的是類注釋,翻譯過來就是:ARouter核心( 外觀模式 )
B:綠色箭頭代表的是一個(gè)線程池,對線程池概念不是很清楚的朋友可以點(diǎn)進(jìn)?必須要理清的Java線程池?先了解一下
C:藍(lán)色箭頭代表的是 這個(gè)_ARouter init()實(shí)際是調(diào)用的LogisticsCenter里面的init方法。
首先,什么是外觀模式?
簡單點(diǎn)理解就是,通過創(chuàng)建一個(gè)統(tǒng)一的類,用來包裝子系統(tǒng)中一個(gè)或多個(gè)復(fù)雜的類,客戶端可以通過調(diào)用外觀類的方法來調(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太長,我就分別截圖)


通過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的對應(yīng)字段中。也就是,如果類名前綴符合文件拼接規(guī)則,比如為com.alibaba.android.arouter.routes.ARouter$$Root的文件,就將其添加到具體的Warehouse里面的集合中。也就是對應(yīng)的這里

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

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

其中,Warehouse的類注釋寫的非常好,翻譯過來就是:路由元數(shù)據(jù)和其他數(shù)據(jù)的存儲(chǔ)。這個(gè)類本質(zhì)就是路由文件映射表。里面提供了各種HashMap集合(Map不允許重復(fù)的key),去存儲(chǔ)SP存儲(chǔ)的值。
我們再看看這里存儲(chǔ)攔截器的集合,UniqueKeyTreeMap,這個(gè)類是存儲(chǔ)攔截器的,我們看看它做了什么?

原來,這個(gè)UniqueKeyTreeMap,就是繼承了TreeMap,哦,我們知道TreeMap是一種有序的集合(底層幫我們排序)所以數(shù)值越小,它就優(yōu)先添加攔截器。這樣也就解釋了為什么攔截器屬性值設(shè)置的越低,優(yōu)先級越高。
綜上,針對?_ARouter init( )?這個(gè)初始化的源碼我們可以得知以下:
A:初始化這一操作,表面上是_ARouter ,實(shí)則是LogisticsCenter 在幫我們管理邏輯
B:LogisticsCenter 內(nèi)部通過先存儲(chǔ)SP,然后遍歷、匹配(滿足條件則添加到具體的集合中,按照文件的前綴不同,將他們添加到路由映射表Warehouse的groupsIndex、interceptorsIndex、providersIndex?中)
C:具體的路由清單是Warehouse ,不僅保存實(shí)例,還給我們提供了緩存。也就是說?同一個(gè)目標(biāo)(RouteMeta、IProvider、IInterceptor)僅會(huì)在第一次使用的時(shí)候創(chuàng)建一次,然后緩存起來。后面都是直接使用的緩存。
初始化還有一個(gè)API,_ARouter.afterInit( ) ; 這個(gè)API在上面的截圖也有,那么這個(gè)API是用來干什么的?我們點(diǎn)進(jìn)去看看。

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

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


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

這個(gè)類注釋翻譯過來就是:預(yù)處理路徑。這個(gè)接口是IProvider的子類
讓我們在看回綠色箭頭,其中,navigation(clazz)這種方式是屬于根據(jù)類型查找,而build(path)是根據(jù)名稱進(jìn)行查找。如果應(yīng)用中沒有實(shí)現(xiàn)PathReplaceService這個(gè)接口,則pService=null。PathReplaceService可以對所有的路徑進(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è)分號的內(nèi)容。
分析完了build,我們在看看navigation

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


其中,navigation - 1這幅圖中的紅色矩形代表的意思是,如果(兩個(gè))路徑?jīng)]寫對,ARouter會(huì)Toast提示客戶端,路徑不對。
navigation - 2這幅圖中的紅色矩形代表的是忽略攔截器。interceptorService實(shí)例化對象的時(shí)機(jī),是在_ARouter這類中的afterInit( )進(jìn)行實(shí)例化的。這幅圖中的藍(lán)色矩形和箭頭的方法真實(shí)邏輯是調(diào)用了下圖的方法:


通過這段代碼我們可以得知:
1:如果navigation()不傳入Activity作為context,則使用Application作為context
2:內(nèi)部使用了Intent來進(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)的判斷
看到了這里,我們在看回navigation - 2這幅圖。如果(兩個(gè))路徑匹配的話,會(huì)執(zhí)行到截圖中的藍(lán)色矩形,點(diǎn)進(jìn)藍(lán)色矩形里面去看看(也就是?LogisticsCenter.completion( Postcard )):



首先,根據(jù)path在Warehouse.routes映射表中查找對應(yīng)的RouteMeta。但是,第一次加載的時(shí)候,是沒有數(shù)據(jù)的。(而第一次加載是在LogisticsCenter.init()中,上面也說了。因此只有`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 圖中,對?IProvider的子類進(jìn)行初始化 provider.init(mContext); 初始化完畢以后,將IProvider的子類添加進(jìn)Warehouse.providers中;最后將IProvider的實(shí)現(xiàn)類保存在postcard中,因此可以從postcard獲取IProvider的實(shí)例對象,其中,greenChannel()會(huì)忽略攔截器這個(gè)前面也說了。
小結(jié):
在LogisticsCenter.completion這個(gè)方法里面完成了對Warehouse.providers、Warehouse.routes的賦值。那么Warehouse.interceptors又是在哪里賦值的呢?
IProvider有個(gè)子類接口,InterceptorService(在文章的上面,也簡單提到過這個(gè)接口)

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

在InterceptorServiceImpl這個(gè)類里面的紅色矩形中,完成了具體攔截器的初始化以及將攔截器添加到Warehouse.interceptors映射表中。
寫到這里,ARouter框架的源碼基本上就分析完畢了??赡苣阏f,這不是只是初始化嘛,是的這只是初始化,但是界面跳轉(zhuǎn)的源碼都涵蓋在上面了,你可能不信,我們點(diǎn)擊跳轉(zhuǎn)的API去看看:

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

同樣,點(diǎn)擊navigation,也回到了上面的navigation中去了。
源碼基本上就寫到這里,內(nèi)容有點(diǎn)繞。個(gè)人建議自己對著源碼好好過一遍,這樣理解的效果可能會(huì)比較好。
如果這篇文章對您有開發(fā)or學(xué)習(xí)上的些許幫助,希望各位看官留下寶貴的star,謝謝。
Ps:著作權(quán)歸作者所有,轉(zhuǎn)載請注明作者, 商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處(開頭或結(jié)尾請?zhí)砑愚D(zhuǎn)載出處,添加原文url地址),文章請勿濫用、開源項(xiàng)目僅供學(xué)習(xí)交流、也希望大家尊重筆者的勞動(dòng)成果,謝謝。