本文已獨家授權 郭霖 ( guolin_blog ) 公衆号釋出!
在《
探索Android路由架構-ARouter(一)》中,主要介紹了ARouter架構的基本配置、多種跳轉方案、攔截器的使用等。這一篇文章主要是對該架構的源碼進行分析:
ARouter是通過APT生成代碼在架構内部進行操作,那麼,項目編譯生成的檔案位置在那裡?

項目編譯生成的檔案位置
既然生成了這些源碼,我們就先随便點點看看這些都是啥?
源碼 - 1
源碼 - 2
源碼 - 3
源碼 - 4
這裡簡簡單單随便截圖了APT生成的部分源碼,是不是感覺跟上一篇文章使用到的代碼很多相似性呐~比如攔截器的優先級是1、跳轉比對的路徑也是一樣的、跳轉傳遞的參數、定義的組名等等。既然這麼多一樣的那肯定是在内部某部分進行封裝使用,帶着這個問題我們開始逐漸分析。
首先,我們從該架構使用到的注解開始分析(因為注解是使用這個架構的起點)
注解分析:首先,我們知道要使用ARouter的首先需要在類的注釋上面寫上 @Route 這個注解,點進源碼看看
Route注解
使用該注解标注的類将被自動添加至路由表中。而且,ARouter 并非僅提供頁面(Activity)的路由功能,還可以用來路由子產品想要暴露給其他子產品調用的接口。也就是說 @Route 不僅可用于 Activity 類,還可用于子產品對外接口的實作類。那麼具體它可以實作那些類型?
從上面的源碼1、2、3、4截圖可以看到,除了攔截器,裡面通過實作接口重寫方法,方法裡面都有一個RouteMeta,那麼我們點進去RouteMeta這個類看看:
RouteMeta
其中紅色矩形中的類注釋一目了然、翻譯過來: 它包含基本路由資訊。
截圖中藍色的矩形,RouteType,就是路由的類型。那麼,這款路由架構的具體路由類型又有那些?帶着這個疑問,點進RouteType 源碼看看。
RouterType
首先這是一個枚舉,這些就是架構可以具體使用到的路由類型
說完Route 這個注解,我們在來看看@Interceptor 攔截器注解,
Interceptor 注解
哦,我們發現攔截器的注解就2個方法,第一個是定義優先級的,第二個就是攔截器的名字。
接着,@Autowired 注解:
我們知道這個注解是界面跳轉時參數傳遞用的。Activity 中使用該注解标志的變量,會在頁面被路由打開的時候自動賦予傳遞的參數值。指派的操作可以參考上面的源碼 - 4截圖。
初始化分析:我們知道,ARouter架構使用的第一個步驟,是要先初始化,也就是調用:ARouter.init( Application.this );這個API,那麼,它的初始化究竟是做了那些東西?我們先點進源碼看看:
ARouter.init()
那麼,實際上它調用的是綠色矩形内的代碼,綠色矩形内有兩個初始化,一個是
_ARouter.init(application),還有一個是
_ARouter.afterInit( )。
我們首先點進
_ARouter . init()看看,
_ARouter
A:紅色箭頭的是類注釋,翻譯過來就是:ARouter核心( 外觀模式 )
B:綠色箭頭代表的是一個線程池,對線程池概念不是很清楚的朋友可以點進
必須要理清的Java線程池先了解一下
C:藍色箭頭代表的是 這個_ARouter init()實際是調用的LogisticsCenter裡面的init方法。
首先,什麼是外觀模式?
簡單點了解就是,通過建立一個統一的類,用來包裝子系統中一個或多個複雜的類,用戶端可以通過調用外觀類的方法來調用内部子系統中所有方法。大概意思就是這樣,想深入了解的話可以自行查閱資料。
其次,這個線程池做了什麼功能?
點進去DefaultPoolExecutor這個類看看:
DefaultPoolExecutor
由源碼得知就是建立了一個數組阻塞隊列的線程池
最後,我們點進LogisticsCenter,看看裡面的init方法執行了什麼操作(因為源碼太長,我就分别截圖)
LogisticsCenter - init (1)
LogisticsCenter - init (2)
通過LogisticsCenter - init(1)這幅源碼圖可以得知:如果是Debug模式,則執行從if代碼塊; 如果是Release模式:通過PackageUtils.isNewVersion(context)判斷目前應用是否是第一次啟動。接着,擷取arouter-compiler生成的檔案,然後将該檔案,存儲在sp中,下次啟動應用的時候,直接從sp緩存中讀取。
然後,在LogisticsCenter - init(2)紅色矩形的代碼塊可以得知:首先周遊arouter-compiler生成的檔案,将他們按照類型分别存儲到Warehouse的對應字段中。也就是,如果類名字首符合檔案拼接規則,比如為com.alibaba.android.arouter.routes.ARouter$$Root的檔案,就将其添加到具體的Warehouse裡面的集合中。也就是對應的這裡
對應的檔案
那麼,這個檔案拼接規則(也就是字元串拼接)是?
檔案拼接規則
Warehouse又是什麼?點進源碼看看
Warehouse
其中,Warehouse的類注釋寫的非常好,翻譯過來就是:路由中繼資料和其他資料的存儲。這個類本質就是路由檔案映射表。裡面提供了各種HashMap集合(Map不允許重複的key),去存儲SP存儲的值。
我們再看看這裡存儲攔截器的集合,UniqueKeyTreeMap,這個類是存儲攔截器的,我們看看它做了什麼?
UniqueKeyTreeMap
原來,這個UniqueKeyTreeMap,就是繼承了TreeMap,哦,我們知道TreeMap是一種有序的集合(底層幫我們排序)是以數值越小,它就優先添加攔截器。這樣也就解釋了為什麼攔截器屬性值設定的越低,優先級越高。
綜上,針對
_ARouter init( )這個初始化的源碼我們可以得知以下:
A:初始化這一操作,表面上是_ARouter ,實則是LogisticsCenter 在幫我們管理邏輯
B:LogisticsCenter 内部通過先存儲SP,然後周遊、比對(滿足條件則添加到具體的集合中,按照檔案的字首不同,将他們添加到路由映射表Warehouse的groupsIndex、interceptorsIndex、providersIndex 中)
C:具體的路由清單是Warehouse ,不僅儲存執行個體,還給我們提供了緩存。也就是說 同一個目标(RouteMeta、IProvider、IInterceptor)僅會在第一次使用的時候建立一次,然後緩存起來。後面都是直接使用的緩存。
初始化還有一個API,
; 這個API在上面的截圖也有,那麼這個API是用來幹什麼的?我們點進去看看。
_ARouter.afterInit
首先,它執行個體化了一個InterceptorService。這裡的InterceptorService下面會說。 這裡的build方法,最終傳回的是一個Postcard對象:
build
從源碼可以得知,實際上它傳回的卻是,_ARouter.getInstance().build(path)這個方法,這個方法是一個方法重載(一般用的最多的就是這一個,也就是預設分組,不進行自定義分組),跟進去看看(源碼很長,分為以下兩個截圖):
_ARouter.getInstance().build - 1
_ARouter.getInstance().build - 2
發現這裡有一個PathReplaceService(也就是紅色矩形),這個PathReplaceService又是什麼,點進去看看
PathReplaceService
這個類注釋翻譯過來就是:預處理路徑。這個接口是IProvider的子類
讓我們在看回綠色箭頭,其中,navigation(clazz)這種方式是屬于根據類型查找,而build(path)是根據名稱進行查找。如果應用中沒有實作PathReplaceService這個接口,則pService=null。PathReplaceService可以對所有的路徑進行預處理,然後傳回一個新的值(傳回一個新的String和Uri)。綠色箭頭有一個extractGroup()方法,點進去看看:
extractGroup
extractGroup(path)這個方法,核心邏輯是紅色矩形内的代碼,這個方法主要是擷取分組名稱。切割path字元串,預設為path中第一部分為組名。這就證明了如果我們不自定義分組,預設就是第一個分号的内容。
分析完了build,我們在看看navigation
navigation
繼續點進源碼看看,發現進入了_ARouter這個類裡面的navigation方法:
navigation - 1
navigation - 2
其中,navigation - 1這幅圖中的紅色矩形代表的意思是,如果(兩個)路徑沒寫對,ARouter會Toast提示用戶端,路徑不對。
navigation - 2這幅圖中的紅色矩形代表的是忽略攔截器。interceptorService執行個體化對象的時機,是在_ARouter這類中的afterInit( )進行執行個體化的。這幅圖中的藍色矩形和箭頭的方法真實邏輯是調用了下圖的方法:
通過這段代碼我們可以得知:
1:如果navigation()不傳入Activity作為context,則使用Application作為context
2:内部使用了Intent來進行傳遞
3:如果在跳轉時,設定了flags,且沒有設定Activity作為context,則下面的startActivity()方法會發生錯誤,因為缺少Activity的Task棧;
4:Fragment的判斷根據版本不同進行了相應的判斷
看到了這裡,我們在看回navigation - 2這幅圖。如果(兩個)路徑比對的話,會執行到截圖中的藍色矩形,點進藍色矩形裡面去看看(也就是 LogisticsCenter.completion( Postcard )):
LogisticsCenter.completion - 1
LogisticsCenter.completion - 2
LogisticsCenter.completion - 3
首先,根據path在Warehouse.routes映射表中查找對應的RouteMeta。但是,第一次加載的時候,是沒有資料的。(而第一次加載是在LogisticsCenter.init()中,上面也說了。是以隻有`Warehouse`的`groupsIndex、interceptorsIndex、providersIndex` 有資料),是以這個時候routeMeta=null。是以,這個時候會先從Warehouse.groupsIndex中取出類名字首為com.alibaba.android.arouter.routes.ARouter$$Group$$group的檔案;接着,将我們添加@Route注解的類映射到Warehouse.routes中;然後将已經加載過的組從Warehouse.groupsIndex中移除,這樣也避免了重複添加進Warehouse.routes;注意,這個時候Warehouse.routes已經有值了,是以重新調用本方法(completion(postcard))執行了else代碼塊。
然後,LogisticsCenter.completion - 2 圖中就是具體的設定屬性值;然後在 LogisticsCenter.completion - 3 圖中,對 IProvider的子類進行初始化 provider.init(mContext); 初始化完畢以後,将IProvider的子類添加進Warehouse.providers中;最後将IProvider的實作類儲存在postcard中,是以可以從postcard擷取IProvider的執行個體對象,其中,greenChannel()會忽略攔截器這個前面也說了。
小結:
在LogisticsCenter.completion這個方法裡面完成了對Warehouse.providers、Warehouse.routes的指派。那麼Warehouse.interceptors又是在哪裡指派的呢?
IProvider有個子類接口,InterceptorService(在文章的上面,也簡單提到過這個接口)
InterceptorService
是以InterceptorServiceImpl也是IProvider的子類;在LogisticsCenter.completion()中,有provider.init(context)的初始化 ;那麼,我們看看InterceptorServiceImpl這個攔截器的實作類,
InterceptorServiceImpl
在InterceptorServiceImpl這個類裡面的紅色矩形中,完成了具體攔截器的初始化以及将攔截器添加到Warehouse.interceptors映射表中。
寫到這裡,ARouter架構的源碼基本上就分析完畢了。可能你說,這不是隻是初始化嘛,是的這隻是初始化,但是界面跳轉的源碼都涵蓋在上面了,你可能不信,我們點選跳轉的API去看看:
簡單跳轉
點選build,又跳進熟悉的build裡面來了:
同樣,點選navigation,也回到了上面的navigation中去了。
源碼基本上就寫到這裡,内容有點繞。個人建議自己對着源碼好好過一遍,這樣了解的效果可能會比較好。
如果覺得這篇文章對你有幫助,希望點下一個小小的star,謝謝。
Ps:著作權歸作者所有,轉載請注明作者, 商業轉載請聯系作者獲得授權,非商業轉載請注明出處(開頭或結尾請添加轉載出處,添加原文url位址),文章請勿濫用、開源項目僅供學習交流、也希望大家尊重筆者的勞動成果,謝謝。