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

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

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

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

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

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

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

這里簡簡單單隨便截圖了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)源碼看看

Route注解

使用該注解標(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è)類看看:

RouteMeta

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

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

RouterType

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

說完Route 這個(gè)注解,我們在來看看@Interceptor?攔截器注解,

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)源碼看看:

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:紅色箭頭的是類注釋,翻譯過來就是: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è)類看看:

DefaultPoolExecutor

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

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

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

對應(yīng)的文件

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

文件拼接規(guī)則

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

Warehouse

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

我們再看看這里存儲(chǔ)攔截器的集合,UniqueKeyTreeMap,這個(gè)類是存儲(chǔ)攔截器的,我們看看它做了什么?

UniqueKeyTreeMap

原來,這個(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)去看看。

_ARouter.afterInit

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

build

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

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

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

PathReplaceService

這個(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

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

分析完了build,我們在看看navigation

navigation

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

navigation - 1
navigation - 2

其中,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 )):

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

首先,根據(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è)接口)

InterceptorService

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

InterceptorServiceImpl

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

寫到這里,ARouter框架的源碼基本上就分析完畢了??赡苣阏f,這不是只是初始化嘛,是的這只是初始化,但是界面跳轉(zhuǎn)的源碼都涵蓋在上面了,你可能不信,我們點(diǎn)擊跳轉(zhuǎn)的API去看看:

簡單跳轉(zhuǎn)

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

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)成果,謝謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容