1. ARouter 原理概述
ARouter 是阿里開源的一款幫助 Android APP 進行組件化改造的路由框架戒财,可以實現(xiàn)在同一個項目中互不依賴的的模塊的 Activity 之間跳轉。
ARouter 的路由
、參數(shù)
和攔截器
都是用注解來標注的。注解在 Retrofit、Dagger 和 EventBus 中都有使用,注解分為運行時注解
和編譯時注解
籽慢。
ARouter 的跳轉是基于路由表 RouterMap
實現(xiàn)的,負責生成路由表
的是 RouteProcessor
猫胁,負責加載路由表
的是 LogisticsCenter
或 RegisterTransform
箱亿。
RouteProcessor 是一個注解處理器,是 AbstractProcessor
的子類弃秆。編譯時注解
是依賴注解處理工具 APT
(Annotation Processing Tool)實現(xiàn)的届惋,用于在編譯時掃描和處理注解,通過 APT 我們能少寫很多模板代碼菠赚。在編譯時脑豹,編譯器會檢查 AbstractProcessor
的子類,并調用 AbstractProcessor 的子類的 process() 方法衡查,然后把添加了注解的元素都傳到 process() 方法中瘩欺,這樣我們就可以在 process() 函數(shù)中生成新的 Java 類文件。
在 RouteProcessor 的 process() 方法中峡捡,會調用 parseRoutes()
方法击碗,parseRoutes() 方法會用 JavaPoet
API 來生成 Java 代碼筑悴,具體的代碼就是 Activity 等類的 Class 信息。除了 RouteProcessor 稍途,ARouter 中還有參數(shù)注解處理器 AutowiredProcessor 和攔截器注解處理器 InterceptorProcessor 阁吝,它們的原理和 RouteProcessor 是一樣的。
在 _ARoute 的 navigation()
方法中械拍,首先會處理預處理服務
突勇,然后會讓 LogisticsCenter
填充 Postcard
中的信息,如果 LogisticsCenter 沒有找到對應的路由信息的話坷虑,就會走降級策略
的邏輯甲馋,如果 LogisticsCenter 找到對應的路由信息的話,就會判斷是不是走綠色通道
迄损,如果不走綠色通道的話就由攔截器鏈
決定要不要跳轉定躏。如果走綠色通道的話,就直接按 Fragment 和 Activity 等不同的類型進行跳轉芹敌。
預處理服務
具體就是一個 PretreatmentService
接口痊远,只要定義一個實現(xiàn)了這個接口的類,并給這個類加一個 @Route
注解就可以使用了氏捞,預處理服務的作用碧聪,是做一些跳轉的時候,在加載路由表前的判斷液茎。
降級策略
的作用是跳轉路由的信息缺失的時候逞姿,要做的事情,比如說給用戶彈一個錯誤提示或記錄錯誤日志等捆等,降級策略對應的是一個 DegradeService 接口滞造,定義一個實現(xiàn)這個接口的類,并添加上 @Route 注解就可以使用降級策略了栋烤。
綠色通道
的作用就是判斷要不要走攔截器鏈断部,比如說我們定義了一個登陸攔截器,但是某個頁面不需要做這個判斷班缎,就可以走綠色通道,走綠色通道只要在調用 build()
方法后調用 greenChannel()
方法就可以了她渴。
攔截器
具體就是一個添加了 @Interceptor
注解并實現(xiàn)了 IInterceptor
接口的類达址,通過攔截器我們能做一些類似登錄態(tài)判斷等邏輯。
負責生成路由表的是 LogisticsCenter
或 RegisterTransform
趁耗,LogisticsCenter 注冊路由表的方式是在運行時通過 ClassUtils
加載 dex
文件中的內容径簿,然后再通過反射初始化這些類的信息锄贷,并保存到倉庫 Warehouse
中。Dex 文件是 Android 平臺的可執(zhí)行文件勿决,類似于 Windows 中的 exec 文件,每個 APK 安裝包中都有 dex 文件究驴,dex 文件中包含了 app 的所有源碼,反編譯后能看到對應分 java 源碼驶忌。
LogisticCenter 中有一個 loadRouterMap() 方法擦剑,這個方法中默認只有一行代碼,這行代碼就是把 registerByPlugin 字段的值改為 false 哥攘。初始化 ARouter 時创橄,會間接調用到 LogisticsCenter 的 init() 方法,在 LogisticsCenter 的 init() 方法中鬼店,會判斷是否由插件加載路由表,如果不是的話到踏,就在運行時楣富,通過 ClassUtils 從 Dex 文件中加載路由表的信息。如果是由 Gradle 插件來注冊路由表的話伴榔,那么就由 RegisterTransform 從 Jar 文件中讀取路由表的信息纹蝴。 RegisterTransform 繼承了 Transform 類,Transform 是 Android 官方提供的用來修改 class 文件的 API 潮梯,每個 Transform 都是一個 Gradle 任務骗灶,能讀取和處理 jar、aar 和 resource 等資源秉馏,用戶自定義的 Transform 會插在 Transform 隊列的最前面耙旦。
Transform API 可以做很多的事情,比如在所有的 class 文件中插樁萝究,做 UI 免都、內存和網(wǎng)絡方面的性能監(jiān)控。還可以修改某個第三方庫的 class 文件帆竹,修改它的邏輯绕娘。還可以在 Log 中插入當前代碼行數(shù),這樣更容易定位問題栽连。還可以對任何類進行動態(tài)代理险领。還可以實現(xiàn)打印出某個方法的入?yún)⒑统鰠⒌拇a侨舆。
2. ARouter 簡介
ARouter 是阿里
開源的一款幫助 Android App 進行組件化改造的路由框架
,是 Android 平臺中對頁面和服務提供路由功能的中間件
绢陌,可以實現(xiàn)在不同模塊的 Activity 之間跳轉挨下。ARouter 的特點是靈活性強
還能幫助項目解耦
。
靈活性強指的是在一些復雜的業(yè)務場景下脐湾,很多功能都是運營人員動態(tài)配置的臭笆。比如電商系統(tǒng)需要下發(fā)一個活動頁面,App 事先不知道該活動具體的目標頁面秤掌,但如果提前做好了頁面映射愁铺,就可以自由配置
了。
幫助解耦指的是闻鉴,隨著業(yè)務量增長茵乱,我們項目代碼會越來越多,開發(fā)人員之間的協(xié)作也會變得越來越復雜椒拗,而解決這個問題的常見方案是插件化和組件化似将。插件化和組件化的前提是代碼解耦
,解耦后還要保持頁面之間的依賴關系蚀苛,這時就要一套路由機制了在验。
ARouter 支持直接解析標準 URL 跳轉
、多模塊工程使用
堵未、添加多個攔截器
腋舌、單獨作為依賴注入框架
、配置轉場動畫
渗蟹、生成路由文檔
等多個特性块饺。
3. ARouter 架構概覽
ARouter 項目中包含了 API
、編譯器 Compiler
雌芽、插件 Gradle Plugin
和注解 Annotation
4 個模塊授艰。
API 模塊由launcher
、core
世落、exception
淮腾、thread
、facede
屉佳、utils
和base
子模塊組成谷朝。
launcher,包含了啟動器 ARouter
武花,core 包含物流中心 LogsticsCenter
和倉庫 Warehouse
等類圆凰。exception 包含了一些異常類。thread 包中包含了CancellableCountDownLatch
体箕,ARRouter 的攔截器鏈
是放在子線程中執(zhí)行的专钉,就用到了它挑童。facede 模塊包含了導航回調 NavigationCallback
和 IInterceptor
等接口。utils 包含了 ARouter 自定義的日志打印器
等工具類跃须。base 包下只有一個用于保存攔截器的 UnitqueKeyTreeMap
炮沐;
ARouter 的 Compiler 模塊包含用于生成路由表
的類,@Autowired
回怜、@Interceptor
和 @Route
注解對應的注解處理器分別是 AutowiredProcessor、InterceptorProcessor 以及 RouteProcessor 等注解處理器都在 Compiler 包中换薄。
Register Plugin
模塊包含了注冊代碼生成器 RegisterCodeGenerator
和 RegisterTransform玉雾,如果我們使用了 ARouter 的路由表加載插件,那這個路由表就會由 Register 插件加載轻要。
Annotation
模塊只包含了一些 @Autowired 等注解和路由類型 RouteType
等枚舉類复旬。
4. ARouter 基本用法
接下來的代碼會統(tǒng)一用 Kotlin 來演示。
1. 添加依賴與配置
2. 聲明路徑
使用 ARouter 要用 @Route
注解聲明跳轉目標的路徑冲泥,在這里要注意驹碍,最前面的斜杠是不能少的,而且路徑至少有兩級凡恍。
group 是可選的志秃,ARouter 內部會對 path 進行分組,以下面這段代碼為例嚼酝,如果不傳 group 的話浮还,那 ARouter 會把 goods 作為該路徑的 group ,否則 taobao 就是該路徑所屬的 group 闽巩。
3. 初始化 ARouter
打印日志和開啟調試模式必須寫 在init() 之前钧舌,否則這些配置在初始化過程中將無效。
4. 跳轉
在跳轉時涎跨,我們也可以傳 group洼冻,比如這個例子中傳了 taobao,那就會跳轉到 taobao 分組下的商品詳情頁隅很,不傳的話撞牢,就會跳到 goods 分組下的詳情頁。
5. 明信片 Postcard
在講 ARouter 的路由表生成原理前外构,我們先來看下在路由表生成和加載過程中非常重要的 RouteMeta
與 Postcard
普泡。當我們調用 ARouter.getinstance().build()
時,其實是在創(chuàng)建一個明信片 Postcard
對象审编,withXXX() 和 navigation() 等方法就是它的方法撼班,Postcard 的 navigation() 方法最終調用的是 _ARouter 的 navigation() 方法,關于 _ARouter 的實現(xiàn)后面會講垒酬。
按 Postcard 的注釋來說砰嘁,它是路線圖的容器件炉,我們可以把它看做是一張包含了收件人信息以及特定內容的明信片。
Postcard 繼承了 RouteMeta矮湘,RouteMeta 是路由表的內容斟冕,而 Postcard 則包含了在跳轉時的傳參和動畫等信息,我們先來看下 RouteMeta缅阳。
5.1 RouteMate
RouteMeta 包含了跳轉路線的基本信息磕蛇,從名字上來看就是路線的元信息,元信息也就是關于信息的信息十办,RouteMeta 包含了以下字段秀撇。
- 路線類型 type
- 路線原始類型 rawType
- 終點 destination
- 路徑 path
- 路線組 group
- 優(yōu)先級 int
- 標志 extra
- 參數(shù)類型 paramsType
- 路線名 name
- 注入配置 injectConfig
1. 路線類型
路線類型 RouteType 是一個枚舉類,有下面幾種類型向族。
Activity
Service
-
Provider
也就是自定義服務 IProvider呵燕;
ContentProvider
Fragment
Broadcast
Method
-
Unknown
未知路線;
其中 Service件相、Broadcast 和 Method 是未實現(xiàn)的再扭。
2. 路線原始類型
路由原始類型 rawType 的類型為 Element,關于 Element 夜矗,在后面會進一步講解泛范,這里只需要知道 Element 中包含了跳轉目標的 Class 信息,是由路由處理器 RouteProcessor 設定的侯养。
3. 終點
終點 destination 就是聲明了 @Route 的跳轉目標的 Class 敦跌,比如目標 Activity 和 Fragment 的 Class,這個信息也是由 RouteProcessor 設定的逛揩。
4. 路徑和路線組
如果我們在 @Route 中只設定了路徑柠傍,比如 path = /goods/details ,那么 goods 就是 group 辩稽,details 就是路徑 path 惧笛。
如果我們設置了 @Route 的 group 的值,比如 group = taobao 逞泄,那么 path 的 group 就是 taobao患整。
5. 優(yōu)先級
優(yōu)先級在 @Route 中無法設定,是給攔截器用的喷众,priority 的值越小各谚,攔截器的優(yōu)先級就越高。
攔截器鏈的實現(xiàn)類為 InterceptorServiceImpl到千,它在調用攔截器的 onInteceptor() 方法時昌渤,就是按照 priority 的順序來獲取攔截器,然后逐個調用的憔四。
6. 標志
這個 extra 是路由文檔 RouteDoc 的標志膀息,文檔中包含了路由和服務相關的信息 般眉,默認不會生成對應的文件,如果想生成的話潜支,可以在注解處理參數(shù)中設置生成文檔甸赃。
-
添加參數(shù)
如果我們想查看路由文檔,可以 AROUTER_MODULE_NAME 后面添加:
AROUTER_GENERATE_DOC: "enable"
-
文檔路徑
build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
路由文檔的大致內容如下冗酿。
7. 參數(shù)類型
paramsType 也就是參數(shù)類型埠对,對于我們跳轉時設定的參數(shù),ARouter 會根據(jù)不同的類型給它們一個枚舉值裁替,然后取值時鸠窗,再根據(jù)不同的類型調用 Intent 的 getXXXExtra() 等方法。
5.2 Postcard
Postcard 中包含如下字段胯究。
- 統(tǒng)一資源標識符 uri
- 標簽 tag
- 傳參 mBundle
- 標志 flags
- 超時時間 timeout
- 自定義服務 provider
- 是否走綠色通道 greenChannel
- 序列化服務 serializationService
- 轉場動畫 optionsCompat
- 進入與退出動畫 enterAnim/exitAnim
下面我們來看下這些字段的作用。
1. uri
在 ARouter 中躁绸,我們可以用 URI 作為路徑跳轉裕循,ARouter 會用路徑替換服務 PathReplaceService 替換路徑,這個服務沒有默認實現(xiàn)净刮,如果我們需要為不同的協(xié)議和主機替換不同的路徑時剥哑,就要自己實現(xiàn)這個服務。
如果我們沒有實現(xiàn)這個服務淹父,那 scheme 和 主機地址等信息是沒有意義的株婴,ARouter 默認情況下只會對路徑和參數(shù)進行解析,在上面的例子中暑认,路徑就是 moduleA/second 困介,參數(shù)為 name=老王。
2. tag
tag 用于在 NavigationCallback 的 interrupt() 方法中獲取異常信息蘸际。
在攔截器鏈 InterceptorServiceImpl 中,當我們在自定義的攔截器中調用 onInterrupt() 方法時,InterceptorServiceImpl 會創(chuàng)建一個 HandlerException 汰规,并把它作為 Postcard 的 tag 醇蝴,然后監(jiān)聽跳轉結果的地方,就能在 onInterrupt() 方法中通過獲取 Postcard 的 tag 信息來獲取異常信息导坟。
我們在攔截器中傳入的 exception 為空的話屿良,那么 HandlerException() 的 message 默認為 "No Message." 。
3. mBundle
我們調用 withString() 等方法設置要傳遞給跳轉目標的數(shù)據(jù)時惫周,這個數(shù)據(jù)就是放在 mBundle 中的尘惧。
4. flags
我們調用 withFlag() 設定 Activity 的啟動標志時,這個標志就會賦值給 flags 字段闯两。
5. timeout
攔截器鏈處理跳轉事件是放在 CountDownLatch 中執(zhí)行的褥伴,超時時間默認為 300 秒谅将,也就是 5 分鐘,所以我們在攔截器中不要進行時間太長的耗時操作重慢。
6. provider
當我們實現(xiàn)了自定義服務時饥臂,參數(shù)注解處理器 AutowiredProcessor 會為各個路徑創(chuàng)建一個實現(xiàn)注射器 ISyringe 接口的類,在這個類的 inject() 方法中似踱,調用了 ARouter.getInstance().navigation(XXXService.class) 隅熙,當 LogisticsCenter 發(fā)現(xiàn)這是一個 Provider 時,就會通過反射創(chuàng)建一個 Provider 實例核芽,然后設置給 Postcard 囚戚,再進行跳轉。
7. greenChannel
所謂綠色通道轧简,就是不會被攔截器鏈處理的通道驰坊,自定義服務 IProvider 和 Fragment 就是走的綠色通道。
如果我們想讓某個跳轉操作跳過攔截器哮独,可以在 navigation() 前調用 greenChannel() 方法拳芙。
8. serializationService
當我們調用 withObject() 方法時,ARouter 就會獲取我們自己自定義的序列化服務 SerializationService皮璧,然后調用該服務的 object2Json() 方法舟扎,再把數(shù)據(jù)轉化為 String 放入 bundle 中。
6. ARouter 路由表生成原理
看完了 RouteMeta 和 Postcard悴务,接下來我們來看下 RouteProcessor 的路由表生成流程睹限。
6.1 注解處理流程
關于 Android Gradle 的 和 Javac 這里不會展開講,在這里我們只需要理解在對 @Route 等注解處理前讯檐,是由 Android Gradle 的 AndroidJavaCompiler 來發(fā)起編譯操作羡疗,然后讓 Javac 去調用 RouteProcessor 等注解處理器對注解進行處理的。
6.2 路由解析流程
RouteProcessor 對于聲明了 @Route 注解的類的處理大致可分為下面 4 個步驟
- 獲取路由元素
- 創(chuàng)建路由元信息
- 把路由元信息進行分組
- 生成路由文件
1. 獲取路由元素
這里的元素指的是 javax 包中的 Element 别洪,Element 表示 Java 語言元素顺囊,比如字段、包蕉拢、方法特碳、類以及接口。
process() 方法會接收到 annotations 和 roundEnv 兩個參數(shù)晕换,annotations 就是當前處理器要處理的注解午乓,roundEnv 就是運行環(huán)境 JavacRoundEnvironment。
JavacRoundEnvironment 有一個 getElementsAnnotatedWith() 方法闸准,RouteProcessor 在處理注解時首先會用這個方法獲取元素益愈。
2. 創(chuàng)建路由元信息
這里說的路由元信息指的是 RouteMeta,RouteProcessor 會把聲明了 @Route 注解的的 Activity、Provider蒸其、Service 或 Fragment 和一個 RouteMeta 關聯(lián)起來敏释。
當元素類型為 Activity 時,RouteProcessor 會遍歷獲取 Activity 的子元素摸袁,也就是 Activity 的成員變量钥顽,把它們放入注入配置 RouteMeta 的 injectConfig 中,當我們配置了要生成路由文檔時靠汁,RouteProcessor 就會把 injectConfig 寫入到文檔中蜂大,然后就對元信息進行分組。
3. 把路由元信息進行分組
在 RouteProcessor 中有一個 groupMap蝶怔,在 RouteMeta 創(chuàng)建好后奶浦,RouteProcessor 會把不同的 RouteMeta 進行分組,放入到 groupMap 中踢星。
拿路徑 /goods/details 來說澳叉,如果我們在 @Route 中沒有設置 group 的值,那么 RouteProcessor 就會把 goods 作為 RouteMeta 的 group 沐悦。
4. 生成路由表
當 RouteProcessor 把 RouteMeta 分組好后耳高,就會用 JavaPoet 生成 Group、Provider 和 Root 路由文件所踊,路由表就是由這些文件組成的,JavaPoet 是 Square 開源的代碼生成框架概荷。
生成路由文件后秕岛,物流中心 LogisticsCenter 需要用這些文件來填充倉庫 Warehouse 中的 routes 和 providerIndex 等索引,然后在跳轉時根據(jù) routes 和索引來跳轉误证。
關于 LogisticsCenter 和 Warehouse 在后面會講继薛,下面我們來看下路由文件的內容。
RouteProcessor 生成的路由文件位于build/generated/source/kapt/(debug/release)/com/alibaba/android/arouter/routes愈捅。
以 ARouter 示例項目中的一個路由文件為例遏考,RouteProcessor 創(chuàng)建的 RouteMeta 會轉化為下面這樣的文件。
其他的路由文件也是大同小異的蓝谨,所有內容都是由 RouteProcessor 填充的灌具。
7. ARouter 跳轉原理
看完了路由表生成流程,下面我們來看下 ARouter 的跳轉原理譬巫。
當我們調用 Postcard 的 navigation()
方法時咖楣,Postcard 會調用 _ARouter 的 navigation() 方法,然后 _ARouter 才會去加載路由表
芦昔,下面看下 navigation() 的處理流程诱贿。
_ARouter 的 navigation() 方法有下面兩種重載。
navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
navigation(Class<? extends T> service)
7.1 navigation()
第一種重載是進行跳轉的,第二種重載是用來創(chuàng)建服務的珠十,下面我們來看第一種重載的實現(xiàn)料扰。
_ARouter 的 navigation() 方法的大致處理流程如下。
_ARouter 的 navigation() 首先會根據(jù)我們實現(xiàn)的預處理服務
焙蹭,判斷是否繼續(xù)往下處理晒杈,不往下處理則中斷跳轉流程。
如果預處理服務返回的是 true
壳嚎,那么 navigation() 方法就會加載路由表
桐智,把 RouteMeta
的信息填充到 Postcard 中,比如終點 destination
等信息烟馅,這個操作是由物流中心 LogisticsCenter
做的说庭。
假如 LogisticsCenter 在完善明信片的過程中遇到了異常
,比如找不到路徑對應的目標郑趁,那么就會調用降級策略
刊驴,我們可以在降級策略中顯示錯誤提示等信息。
-
攔截器鏈
在明信片完善信息后寡润,navigation() 就會把跳轉事件交給攔截器鏈處理捆憎;
-
按類型跳轉
在攔截器鏈處理完成,并且沒有中斷跳轉時梭纹,navigation() 就會按照路徑類型跳轉到不同的頁面或調用自定義服務躲惰;
1. 實現(xiàn)預處理服務
預處理服務可以讓我們在 ARouter 進行跳轉前,根據(jù) PostCard 的內容判斷是否要獨立地對這次跳轉進行處理变抽,是的話則在 onPretreatment() 中返回 false 即可础拨。
2. 預處理服務處理流程
在 navigation() 中,首先會調用預處理服務的 onPretreamtn() 方法绍载,判斷是否要繼續(xù)往下處理诡宗,如果返回結果為 false ,則不再往下處理击儡,也就是不會進行跳轉等操作塔沃。
7.2 完善明信片
調用完預處理服務后,_ARouter 就會用物流中心 LogisticsCenter 來加載路由表阳谍,路由表也就是 RouteProcessor 生成的路由文件蛀柴。
1. 獲取路由元信息
在 _ARouter 初始化時,會把 LogisticsCenter 也進行初始化矫夯,而 LogisticsCenter 的初始化方法中名扛,會讀取 RouteProcessor 創(chuàng)建好的路由表,然后放到對應的索引 index 中茧痒。
有了索引肮韧,當 _ARouter 調用 LogisticsCenter 的 completion() 方法時,就可以用索引從 Warehouse 的 routes 中獲取路由元信息。
如果 LogisticsCenter 根據(jù)索引查找不到對應的 RouteMeta弄企,那就說明 routes 還沒有被填充超燃,這時 LogisticsCenter 就會獲取 group 的 RouteMeta,然后把 group 下的路徑填充到 routes 中拘领,然后再調用一次 completion() 意乓,這時就可以取填充明信片的信息了。
索引是在 LogsticsCenter 初始化的時候加載的约素,后面在路由表加載原理的時候會講到届良。
2. 填充明信片信息
我們在調用 ARouter.getInstance().build() 方法時,就是創(chuàng)建了一張 Postcard圣猎,但是這張 Postcard 的信息是不完整的士葫。
比如 Postcard 中有一個 destination 字段,destination 表示跳轉目標的 Class 信息送悔,跳轉 Activity 要用 Intent慢显,destination 就是 Intent 的構造函數(shù)的第二個參數(shù)。
而 LogisticsCenter 就要負責把從路由表獲取到的 destination 信息填充到這張明信片中欠啤,有了一張信息完整的明信片荚藻,才能進行后續(xù)的跳轉操作。
3. 初始化 Provider
填充完了 Postcard 的信息后洁段,LogisticsCenter 會根據(jù) Postcard 的類型來做不同的操作应狱,如果是 Provider 的話,就會調用 Provider 的初始化方法祠丝,并且把 Postcard 設為綠色通道疾呻。
如果是 Fragment 的話,那就只把 Postcard 設為綠色通道纽疟,如果是其他類型,則不設為綠色通道憾赁,這里說的綠色通道污朽,其實就是說 Provider 和 Fragment 是跳過攔截器鏈的。
2.1.3 降級策略
所謂的降級策略龙考,其實就是跳轉失敗時蟆肆,我們能夠跳轉到別的頁面,比如一個跳轉失敗提示頁晦款。
假如 ARouter 在完善明信片信息的過程中遇到了異常炎功,
1. 降級策略處理流程
調用完預處理服務后,navigation() 就會通過物流中心 LogisticsCenter 來填充 PostCard 內容缓溅,如果在填充過程中遇到了異常蛇损,就會調用降級服務,關于 LogisticsCenter 后面會講。
2. 自定義降級策略
在自定義降級策略時淤齐,要注意 context 可能會空股囊,要使用 Context? 。
7.3 攔截器
攔截器可以用來在跳轉過程中處理事件更啄,比如做登陸檢查稚疹,攔截器會在跳轉之間執(zhí)行,多個攔截器會按優(yōu)先級順序依次執(zhí)行祭务。
1. 實現(xiàn)攔截器
實現(xiàn)攔截器時内狗,我們要調用 onContinue() 或 onInterrupt() 方法,至少需要調用其中一種方法义锥,否則不會繼續(xù)路由柳沙。
onContinue() 這個方法表示處理完成,交換控制權給 ARouter缨该;
如果不想繼續(xù)跳轉偎行,可以用 onInterrupt() 方法,傳一個異常贰拿,以中斷路由流程蛤袒;
2. 攔截器處理流程
navigation() 在完善 Postcard 信息后,就會判斷該 Postcard 是否通過綠色通道來處理膨更,綠色通道就是不經(jīng)過攔截器鏈的通道妙真,關于攔截器鏈的實現(xiàn)在后面會講。
在 LogisticsCenter 的 completion() 中荚守,中會把路由類型為 Provider 和 Fragment 的路線設為綠色通道珍德,如果我們想讓目標頁面跳過攔截器鏈,就可以在 navigation() 方法前調用 greenChannel() 方法矗漾。
7.4 按類型跳轉
當處理完攔截器后锈候,navigation() 中就會調用 _navigation() 方法,這也是具體進行跳轉的方法敞贡。
在這個方法中泵琳,會根據(jù) Postcard 的路由類型 RouteType 來判斷怎么跳轉,
1. Activity
當 RouteType 為 Activity 時誊役,啟動 Activity 的流程和我們平時啟動 Activity 的流程是一樣的获列,創(chuàng)建 Intent、傳入 destination蛔垢、設置 fragment 和重寫動畫等击孩,最終調用 startActivity() 啟動目標頁面。
2. Fragment / Broadcast / ContentProvider
當跳轉目標為 Fragment鹏漆、Broadcast 或 ContentProvider 時巩梢,會通過 destination 用反射創(chuàng)建實例创泄,如果是 Framgent ARouter 還會為它設置要傳遞給目標 Fragment 的參數(shù),然后返回實例且改。
3. Provider
如果跳轉目標為 Provider验烧,也就是自定義服務的話,就對應了后面講 ARouter 自定義服務時講的“通過依賴查找發(fā)現(xiàn)服務”又跛。
7.5 跳轉回調
在調用 navigation() 跳轉時碍拆,我們可以在 navigation() 中傳入 NavigationCallback 監(jiān)聽跳轉過程中發(fā)生的事件。
-
onLost()
無法查找到跳轉目標慨蓝;
-
onFound()
找到了跳轉目標感混;
-
onInterrupt()
攔截器中斷了跳轉;
-
onArrival()
已打開跳轉目標礼烈;
除了 NavigationCallback 弧满,我們也可以用 NavCallback 監(jiān)聽跳轉中發(fā)生的事件,NavCallback 是 ARouter 中實現(xiàn)了 NavigationCallback 的一個抽象類此熬,使用 NavCallback 不會強制要求我們重寫所有方法庭呜,只要求重寫 onArrival() 方法。
8. ARouter 路由表加載原理
所謂的加載路由表犀忱,其實就是加載 RouteProcessor 生成的類文件
募谎。
在我們調用 ARouter 的 init()
方法時,ARouter 會調用 LogisticsCenter
的 init() 方法阴汇,在 LogisticsCenter 的 init() 方法中数冬,會判斷當前路由表加載方式是否為插件,不是的話則從 Dex 中加載路由表
搀庶,是的話則由插件從 Jar 中加載路由表
拐纱。
下面我們來看下怎么通過 Dex 加載路由表。
8.1 從 Dex 中加載路由表
通過類加載路由表的流程大致可分為讀取 Dex 文件
哥倔、從 Dex 文件中讀取路由表
秸架、把路由表保存到本地
以及把路由信息保存到索引
這 4 步。
讀取 Dex 文件
咆蒿,指的是當 LogisticsCenter 發(fā)現(xiàn)沒有用插件加載路由表時东抹,就會用 ClassUtils
讀取路由表。這里說的路由表蜡秽,其實就是 RouteProcessor 生成好的類文件類名
府阀,而讀取 Dex 文件的方式缆镣,就是從源碼目錄(applicationInfo.sourceDir) 中讀取 base apk 的路徑芽突,然后用這個路徑構建一個 DexFile(path) 。
從 Dex 文件中
讀取路由表指的是 ClassUtils
會用 DexFile 讀取 apk 中的類信息董瞻,然后判斷類的包名是否為 com.alibaba.android.arouter.routes
寞蚌,是的話說明這是 ARouter 的注解處理器生成的路由文件田巴,把匹配上的類加入列表中,然后把列表返回給 LogisticsCenter 挟秤。
把路由表保存到本地
指的是當 LogisticsCenter 從 ClassUtils 中獲取到注解處理器生成的類名時壹哺,就會把這些類名保存 SharedPreferences 中,下次就根據(jù) App 版本判斷艘刚,如果不是新版本管宵,就從本地中加載類名,否則就用 ClassUtils 讀取類名攀甚。
把路由信息保存到索引
指的是當 LogisticsCenter 把路由表保存到 SharedPreferences 后箩朴,就會根據(jù)類名的后綴判斷類是 IRouteRoot 、IInterceptorGroup 還是 IProviderGroup 秋度,然后根據(jù)不同的類把類文件的內容加載到索引中炸庞。
8.2 從 Jar 中加載路由表
如果我們想縮短 ARouter 的初始化時間,可以用 ARouter 的 Gradle 插件荚斯,這個插件能自動加載路由表埠居,這樣 ARouter 初始化的時候就不需要讀取類的信息,從而縮短初始化時間事期。
Register 插件從 Jar 文件加載路由表的流程如下滥壕。
當我們運行 App 時,Gradle 就會調用我們依賴的 ARouter Register 插件刑赶,這個插件的執(zhí)行的起點就在 PluginLaunch 的 apply()
中捏浊,PluginLaunch 判斷了只有在運行的項目為 Applicaiton 時,才會加載路由表信息撞叨。
Android Gradle 插件包含了一個 Transform API金踪,這個 API 允許第三方插件在編譯后的類文件轉換為 dex 文件前做處理操作
,而 ARouter 的 Register 插件就實現(xiàn)了一個 RegisterTransform
牵敷。
當 Gradle 的任務管理器 TaskManager 執(zhí)行 TransformTask 時胡岔,就會執(zhí)行 RegisterTransform 的 transform()
方法。在 transform() 方法中枷餐,會接收到一個 TaskManager
傳過來的 TransformInput
集合靶瘸,通過 TransformInput 可以獲取到 Gradle 為我們項目生成的 Jar 和 Class 文件,包括 RouteProcessor 生成的文件
毛肋。
當 RegisterTransform 獲取到了 Class 文件后扁远,就會用 ScanUtils
中的 ScanClassVisitor
訪問 Class 中的字節(jié)碼數(shù)據(jù)缚俏,包括 Class 的包名和類名。ScanClassVisitor 是 ClassVisitor 的子類,ClassVisitor 可以改變修改字節(jié)碼犀填,當 ScanClassVisitor 發(fā)現(xiàn)當前 Class 實現(xiàn)了 IRouteRoot
讥蔽、IIntercetorGroup
或 IProviderGroup
接口時奇徒,這三個接口都是就會把當前 Class 的類名添加到 ScanSetting 的 classList
中。
當 RegisterTransform 把 ScanSetting
中的 classList
初始化完后巍膘,就會用代碼生成器 RegisterCodeGenerator
插入代碼到 Jar 文件中。
在 RegisterCodeGenerator 的 insertInitCodeIntoJarFile()
方法中芋簿,會把 Jar 文件轉化為 JarFile 對象峡懈,然后獲取到 JarFile 中的 JarEntry
,找到 LogisticsCenter与斤,然后把路由表插入到 LogisticsCenter 的 loadRouteMap()
方法中肪康,比如下面這樣。
-
Jar 文件路徑
build/intermediates/transforms/com.alibaba.arouter/debug/41.jar
9. ARouter 進階用法與注意事項
1. 傳參
2. 解析參數(shù)
在使用 @Autowired 注解時撩穿,要注意下面幾點梅鹦。
這里需要注意的是,如果 Activity 或 Fragment 是用 Kotlin 寫的冗锁,那字段就要加上 @JvmField
齐唆,以提供 getter()
和 setter()
給 ARouter 使用。
key 不能為空
冻河,因為會被取做默認值箍邮,否則會出現(xiàn)空指針異常。因為 ARouter 在用 Intent 的 getLongExtra() 等方法獲取參數(shù)時叨叙,這些方法默認值不是包裝類型锭弊,如 Long ,而是基本類型擂错,如 long 味滞。
使用 withObject()
傳遞 List 和 Map 時,接收該對象的地方不能標注具體的實現(xiàn)類類型
钮呀,應聲明為 List 或 Map剑鞍,否則會影響序列化中類型的判斷, 其他類似情況需要同樣處理。
如果你發(fā)現(xiàn)接收不到你想要的參數(shù)時爽醋,可以在 AutowiredServiceImpl
的 autowired()
方法中打個斷點看下遇到了什么異常蚁署。
如果在使用 ARouter 的過程中,出現(xiàn)了編譯失敗但是不知道原因時蚂四,可以用命令查看詳細的編譯日志 gradle aseembleDebug --stacktrace
光戈。
3. 實現(xiàn)序列化服務
ARouter 允許我們設置序列化服務,如果需要傳遞自定義對象遂赠,那只需要新建一個實現(xiàn) SerializationService
的類久妆,并加上 @Route
注解 。我們可以給服務設定不同的分組跷睦,就像下面的 yourservicegroupname
一樣筷弦。
4. 自定義服務
除了序列化等服務以外,ARouter 還允許我們通過實現(xiàn) IProvider 接口實現(xiàn)自定義服務送讲。
首先聲明接口奸笤。
然后實現(xiàn)接口。
ARouter 發(fā)現(xiàn)服務的方式有兩種哼鬓,一是通過依賴注入發(fā)現(xiàn)服務
监右,二是通過依賴查找發(fā)現(xiàn)服務
,推薦通過依賴注入發(fā)現(xiàn)服務异希,比如下面這樣健盒。
helloService 和 helloService2 就是通過依賴注入的方式發(fā)現(xiàn)服務的,也就通過注解標注字段
即可使用称簿,無需主動獲取扣癣。
如果不設置 name 屬性的話,會默認使用 byType
的方式發(fā)現(xiàn)服務憨降,helloService 就是 byType父虑。
Autowired 注解中標注 name 之后,將會使用 byName
的方式注入對應的字段授药。
當同一接口有多個實現(xiàn)的時候士嚎,必須使用byName的方式發(fā)現(xiàn)服務,helloService2 就是 byName悔叽。
下面是通過依賴查找發(fā)現(xiàn)服務的代碼莱衩。主動發(fā)現(xiàn)服務并使用,helloService3 是 byName 娇澎,helloService4 是 byType 笨蚁。
5. 通過 URL 跳轉
如果我們想讓應用直接處理外部 URI,只要在清單文件中進行配置趟庄,再把 url 傳遞給 ARouter 即可括细。
首先設定 host,在 Manifest 中聲明 Activity 的 action 和 category 戚啥,并且在 data 標簽中添加主機 host
和協(xié)議 scheme
勒极。
然后新建一個 Activit y用于監(jiān)聽 Scheme
事件,直接把 url 傳遞給 ARouter 即可虑鼎。
6. 設置轉場動畫
ARouter 提供了 withTransition() 方法來設置跳轉的轉場動畫
7. 獲取 Fragment
獲取 Fragment 時要注意辱匿,當找不到對應路徑的 Fragment 時,會返回 null炫彩,所以這里用空安全的 as? 匾七。
10. 其他
如果你想交流 Android 開發(fā)相關的問題,歡迎加我的微信 oushaoze2015 一起探討江兢,添加時請備注“掘金”昨忆。