在工作的項目中使用到了阿里的開源工具Arouter作為頁面跳轉路由,這里記錄一下自己對于該框架使用一次跳轉的研究。
首先附上github鏈接GitHub - alibaba/ARouter: An android router middleware that help app navigating to activities and custom services.,對于Arouter的適用性、優(yōu)點和用法可以自行了解菱蔬。
在項目中加入了Arouter跳轉的代碼后,運行項目史侣,首先你會發(fā)現(xiàn)一些清單文件拴泌,位置在/build/generated/source/apt/release/com/alibaba/android/arouter/routes/下:
這里的幾個類分別實現(xiàn)了不同的接口,IRouteGroup惊橱、IInterceptorGroup蚪腐、IProviderGroup、IRouteRoot,這里我們以頁面的跳轉分析為主税朴,點開IRouteRoot削茁、IRouteGroup的實現(xiàn)類,你會發(fā)現(xiàn)它們都在loadInto方法對傳入的容器填充了一些數(shù)據(jù)掉房,在IRouteRoot的實現(xiàn)類中傳了IRouteGroup實現(xiàn)類的類名,在IRouteGroup實現(xiàn)類中傳入了一個封裝的RouteMeta的對象:
仔細一看每個RouteMeta的參數(shù)是不是很熟悉慰丛?里面出現(xiàn)了你已配置路徑的頁面名以及注解@Route中設置的path值:
其實在開發(fā)者配置完成后卓囚,ARouter會生成這些清單文件,在調用初始化方法init時會去加載這些清單文件诅病,將配置的頁面緩存起來哪亿,同時你也已經發(fā)現(xiàn)在loadinfo方法中體現(xiàn)的調用關系是存在root、group這些概念的贤笆。
如果你已經了解過Arouter,一定知道Arouter的一個特點:
映射關系按組分類蝇棉、多級管理,按需初始化
在這里按組分類芥永、多級管理其實已經體現(xiàn)出來了篡殷。
看完了清單文件,我們從Arouter的初始化開始入手:
ARouter.init(application);
往下查看埋涧,我們發(fā)現(xiàn)其實是調用了
_ARouter.init(application)
最終加載清單文件是LogisticsCenter.init方法中實現(xiàn)板辽,關鍵代碼如下:
首先我們按照正常的rlease版本的流程,進入else的代碼塊棘催,這里發(fā)現(xiàn)routerMap是從數(shù)據(jù)庫里面拿出來的劲弦,接來下對routerMap的內容className做循環(huán)判斷,這里className的三個判斷條件其實分別是:
ROUTE_ROOT_PAKCAGE +DOT +SDK_NAME +SEPARATOR +SUFFIX_ROOT:
com.alibaba.android.arouter.routes.ARouter$$Root
ROUTE_ROOT_PAKCAGE +DOT +SDK_NAME +SEPARATOR +SUFFIX_INTERCEPTORS:
com.alibaba.android.arouter.routes.ARouter$$Interceptors
ROUTE_ROOT_PAKCAGE+DOT +SDK_NAME +SEPARATOR +SUFFIX_PROVIDERS:
com.alibaba.android.arouter.routes.ARouter$$Providers
找到了清單文件類之后傳入了Warehouse類中的成員變量并表用loadInfo方法醇坝,到此初始化已經結束邑跪,再進入Warehouse中瞧一瞧:
可見對于頁面來說,清單的配置最后被緩存到了WareHouse這個類,頁面只緩存到了節(jié)點不做下級的加載画畅,這也就是之前說的按需加載砸琅,這里只存到了類名并沒有把節(jié)點全部緩存下來。
在發(fā)起一個頁面跳轉的請求的時夜赵,所有的請求都被封裝成了一個Postcard對象明棍,我們先來看看這個對象如何生成的,最開始Arouter調用的是build方法:
_Arouter的build方法返回了一個新的Postcar對象:
看看這個PathReplaceService是如何生成的寇僧,順著navigation方法往下看摊腋,最后在_Arouter的navigation的方法中返回
這里在buildProvider中會對IRouterProvider的緩存中查詢是否注冊了對應的provider,僅僅只做頁面跳轉這里可以不用注冊嘁傀,那么整個方法會拋出異常兴蒸,在catch塊中返回null。那么在build中則會進入build(path, extractGroup(path))方法:
在這個方法中细办,由上面的分析可知這里的pService為空橙凳,所以最簡單的情況就是返回一個新的由path和group決定的Postcard。
再進行跳轉時笑撞,將會調用Postcard的navigation方法岛啸,最終是將自己傳入了_Arouter的navigation方法中:
protected Object navigation(final Context context,final Postcard postcard,final int requestCode,final NavigationCallback callback)?
在該方法中首先調用了LogisticsCenter.completion(postcard),這方法比較長茴肥,一段一段來看坚踩,其實這是個注冊Postcard的方法:
首先從Root容器中去查找有沒有對應的內容,從我們之前的加載過程中看是肯定找不到的瓤狐,那么第二步則從Group中去找瞬铸,找到了之后則把頁面的內容加載到routes容器中,這里是通過之前init緩存的類名找到對應的Group础锐,然后調用Group的loadInfo方法緩存具體的每個節(jié)點嗓节,也就是到了要使用的時候再加載具體的節(jié)點,loadInfo是最開始清單文件中IRouterGroup實現(xiàn)類的方法皆警,具體的填充過程看一下就知道了拦宣,再重新reload。
else代碼塊里對于已經緩存的內容信姓,則把相應的屬性填入Postcard中恢着,也就是把routeMeta轉化為Postcard
在最后會對routeMeta的類型做判斷,在清單文件類中财破,new reoutMeta時會將頁面的類型傳入掰派,所以這里是可以判斷的。
在LogisticsCenter.completion(postcard)完成之后接著判斷是否走的是綠色通道greenChanel(跳過所有的攔截器):
我們先看跳過攔截器的else內的代碼塊左痢,接著調用_navigation()方法:
可見跳轉的地方就在這里啦靡羡,在回到之前是否走綠色通道的邏輯系洛,如果是不跳過攔截器,則會調用interceptorService.doInterceptions(postcard,new InterceptorCallback())略步,這里interceptorService是一個接口描扯,我們找到它的實現(xiàn)類com.alibaba.android.arouter.core.InterceptorServiceImpl瞧一瞧doInterceptions()方法:
著這個方法中首先會判斷是否注冊了攔截器被緩存進來,攔截器的緩存也是在init的操作中已經做過的趟薄,如果有的話則開啟一個線程绽诚,這里攔截器相關都是異步的,因為攔截器的操作可能非常耗時杭煎,其中_excute()方法是攔截器操作的地方恩够,接著看下去:
根據(jù)index依次取出攔截器,并調用process()方法羡铲,而當我們定義一個攔截器的時候蜂桶,最重要的便是重寫IInterceptor的process()方法,那么到這里攔截器是如何運作已經一目了然了:
還有一個問題也切,攔截器是有優(yōu)先級的扑媚,這里又是怎么按照優(yōu)先級攔截,如果斷開后續(xù)攔截又是怎么斷開的雷恃,首先我們看在代碼中定義的攔截器疆股,這里使用了注解去定義該攔截器的優(yōu)先級:
在清單文件中加載攔截器時會將該優(yōu)先級作為key填充到容器中:
在_excute()方法中從緩存中調用攔截器是從index=0作為索引開始的,如果繼續(xù)攔截則在continue中使index+1并遞歸倒槐,這樣就實現(xiàn)了按照優(yōu)先級攔截:
在清單文件中攔截器的加載說明Warehouse中interceptorsIndex存儲的內容以攔截器的優(yōu)先級作為key押桃,攔截器的類名作為value,并且interceptorsIndex是一個TreeMap,而攔截器具體的類在使用的時候是從interceptors中加載過來的导犹,這其實是在InterceptorServiceImpl中完成的,在_Arouter完成init之后馬上會調用一次afterInit():
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
到最后會走到LogisticsCenter.completion()方法羡忘,在之前我們發(fā)現(xiàn)這個方法最后會對routMeta的類型做判斷:
當判斷為IProvider接口類型時會調用init的方法谎痢,回到InterceptorServiceImpl你會發(fā)現(xiàn)這個類實現(xiàn)了InterceptorService接口,而InterceptorService又繼承自IProvider接口卷雕,那么很明顯了节猿,看看InterceptorServiceImpl的init()方法:
Warehouse.interceptors的填充就在這里了。