TheRouter
是一個(gè) Kotlin 編寫字柠,用于 Android 模塊化開發(fā)的一整套解決方案框架窑业。
Github 項(xiàng)目地址與使用文檔詳見 https://github.com/HuolalaTech/hll-wp-therouter-android常柄。
TheRouter 核心功能具備如下能力:
- 頁面導(dǎo)航跳轉(zhuǎn)能力(Navigator)
- 跨模塊依賴注入能力(ServiceProvider)
- 單模塊自動(dòng)初始化能力(FlowTaskExecutor)
- 動(dòng)態(tài)化能力(ActionManager)
- 模塊AAR/源碼依賴一鍵切換腳本
一拐纱、為什么要使用 TheRouter
路由是現(xiàn)如今移動(dòng)端開發(fā)中必不可少的功能哥倔,尤其是企業(yè)級(jí)APP咆蒿,可以用于將Intent
頁面跳轉(zhuǎn)的強(qiáng)依賴關(guān)系解耦沃测,同時(shí)減少跨團(tuán)隊(duì)開發(fā)的互相依賴問題蒂破。
對(duì)于大型 APP 開發(fā),基本都會(huì)選用模塊化(或組件化)方式開發(fā)惧互,對(duì)于模塊間解耦要求更高喊儡。 TheRouter
是一整套完全面向模塊化開發(fā)的解決方案艾猜,不僅能支持常規(guī)的模塊依賴解耦匆赃、頁面跳轉(zhuǎn),同時(shí)提供了模塊化過程中常見問題的解決辦法钱床。例如:完美解決了模塊化開發(fā)后由于組件內(nèi)無法獲取 Application
生命周期與業(yè)務(wù)流程查牌,造成每次初始化與關(guān)聯(lián)依賴調(diào)用都需要跨模塊修改代碼的問題纸颜。
1.1 TheRouter 四大能力
Navigator:
- 支持
Activity
和Fragment
- 支持
Path
與頁面多對(duì)一關(guān)系或一對(duì)一關(guān)系胁孙,可用于解決多端path統(tǒng)一問題 - 頁面
Path
支持正則表達(dá)式聲明 - 支持
json
格式路由表導(dǎo)出 - 支持動(dòng)態(tài)下發(fā)
json
路由表称鳞,降級(jí)任意頁面為H5 - 支持任意
object
跨模塊傳遞(無需序列化冈止,且能保證對(duì)象類型) - 支持頁面跳轉(zhuǎn)攔截處理
- 支持自定義頁面參數(shù)解析方式(例如將
json
解析為對(duì)象) - 支持使用路由跳轉(zhuǎn)到第三方 SDK 中的
Activity
(Fragment
)
ServiceProvider:
- 支持跨模塊依賴注入
- 支持自定義注入項(xiàng)的創(chuàng)建規(guī)則熙暴,依賴注入可自定義參數(shù)
- 支持自定義服務(wù)攔截周霉,單模塊
mock
調(diào)試 - 支持注入對(duì)象緩存俱箱,多次注入 只會(huì)new一次對(duì)象
FlowTaskExecutor:
- 支持單模塊獨(dú)立初始化
- 支持懶加載初始化
- 獨(dú)立初始化允許多任務(wù)依賴(參考
Gradle Task
) - 支持編譯期循環(huán)引用檢測
- 支持自定義業(yè)務(wù)初始化時(shí)機(jī),可以用于解決隱私合規(guī)問題
ActionManager:
- 支持全局回調(diào)配置
- 支持優(yōu)先級(jí)響應(yīng)與中斷響應(yīng)
- 支持記錄調(diào)用路徑厂财,解決調(diào)試期觀察者模式無法追蹤
Observable
的問題
注: FlowTaskExecutor
、ActionManager
后續(xù)會(huì)作為可選能力与斤,提供可插拔
或單獨(dú)使用
的選項(xiàng)(預(yù)計(jì)10月份提供)撩穿。
二食寡、路由方案
目前現(xiàn)有的路由基本上集中于兩種能力的實(shí)現(xiàn):頁面跳轉(zhuǎn)抵皱、跨模塊調(diào)用呻畸,核心技術(shù)方案大體上如圖:
- 開發(fā)階段伤为,對(duì)要使用路由的落地頁或被調(diào)用方法添加注解標(biāo)識(shí)。
- 編譯期解析注解叙甸,生成一系列中間代碼裆蒸,待調(diào)用光戈。
- 應(yīng)用啟動(dòng)后調(diào)用中間代碼完成路由的準(zhǔn)備動(dòng)作久妆。大部分路由會(huì)額外通過
Gradle Transform
跷睦,在編譯期做一次聚合,以提升運(yùn)行時(shí)準(zhǔn)備路由表的效率爹殊。 - 發(fā)起路由跳轉(zhuǎn)時(shí)梗夸,本質(zhì)上就是一次路由表遍歷反症,通過uri獲取到對(duì)應(yīng)的落地頁或方法對(duì)象铅碍,進(jìn)行調(diào)用胞谈。
TheRouter
的頁面跳轉(zhuǎn)憨愉、跨模塊調(diào)用也是如此配紫,但是在設(shè)計(jì)上會(huì)有一些細(xì)節(jié)處理笨蚁。
TheRouter
會(huì)在編譯期根據(jù)注解生成 RouteMap__
開頭的類括细,這些類中記錄了當(dāng)前模塊的所有路由信息奋单,也就是當(dāng)前模塊的路由表览濒。
在最頂層的app
模塊中,通過Gradle
插件应又,將所有aar株扛、源碼中的RouteMap__
開頭的類統(tǒng)一集中到TheRouterServiceProvideInjecter
類中洞就。
后續(xù)應(yīng)用啟動(dòng)后旬蟋,初始化路由時(shí)只需要執(zhí)行TheRouterServiceProvideInjecter
類的方法倾贰,就能沒有任何反射的加載到全部的路由表了躁染。
加載以后的路由表會(huì)被保存到一個(gè)支持正則匹配的 Map
中吞彤,這也是TheRouter
允許多個(gè)path
對(duì)應(yīng)同一個(gè)落地頁的原因饰恕。每當(dāng)發(fā)生頁面跳轉(zhuǎn)時(shí)埋嵌,通過跳轉(zhuǎn)時(shí)的path
俱恶,去Map
中獲取到對(duì)應(yīng)的落地頁信息合是,再正常調(diào)用startActivity()
即可聪全。
三难礼、使用 TheRouter 頁面跳轉(zhuǎn)
3.1 聲明路由項(xiàng)
如果一個(gè)頁面(支持 Activity蛾茉、Fragment)允許被路由打開谦炬,則需要使用注解 @Route
聲明路由項(xiàng),每個(gè)頁面允許聲明多個(gè)路由項(xiàng)散劫,也就是一對(duì)多的能力获搏,極大降低多端路由統(tǒng)一時(shí)的業(yè)務(wù)影響面常熙。
參數(shù)釋義
-
path: 路由path 【必傳】裸卫。
建議是一個(gè)url墓贿。path內(nèi)支持使用正則表達(dá)式(為了匹配效率聋袋,正則必須包含反雙斜杠\)幽勒,允許多個(gè)path對(duì)應(yīng)同一個(gè)Activity(Fragment)啥容。 -
action: 自定義事件【可選】顷霹。
一般用來打開目標(biāo)頁面后做一個(gè)執(zhí)行動(dòng)作泼返,例如自定義頁面彈出廣告彈窗绅喉。 -
description: 頁面描述【可選】柴罐。
會(huì)被記錄到路由表中革屠,方便后期排查的時(shí)候知道每個(gè)path或Activity是什么業(yè)務(wù)。 -
params: 頁面參數(shù)【可選】板甘。
自動(dòng)寫入intent
中盐类,允許寫在路由表中動(dòng)態(tài)下發(fā)修改默認(rèn)值,或通過路由跳轉(zhuǎn)時(shí)代碼傳入呛谜。
@Route(path = "http://therouter.com/home", action = "action://scheme.com",
description = "第二個(gè)頁面", params = {"hello", "world"})
public class HomeActivity extends AppCompatActivity {
}
3.2 發(fā)起頁面跳轉(zhuǎn)
傳入的參數(shù)可以是 String
和8種基本數(shù)據(jù)類型在跳、也可以是Bundle
、Serializable
隐岛、
Parcelable
對(duì)象猫妙,跟 Intent
傳值規(guī)則一致。
同時(shí)也支持為本次跳轉(zhuǎn)的 Intent
添加Flag/Uri/ClipData/identifier
等業(yè)務(wù)特殊參數(shù)割坠。
// 傳入?yún)?shù)可以通過注解 @Autowired 解析成任意類型,如果是對(duì)象建議傳json
// context 參數(shù)如果不傳或傳 null元践,會(huì)自動(dòng)使用 application 替換
TheRouter.build("http://therouter.com/home")
.withInt("key1", 12345678)
.withString("key2", "參數(shù)")
.withBoolean("key3", false)
.withSerializable("key4", object)
.withObject("object", any) // 這個(gè)方法可以傳遞任意對(duì)象,但是接收的地方對(duì)象類型需自行保證一致童谒,否則會(huì)強(qiáng)轉(zhuǎn)異常
.navigation(context);
// 如果傳入 requestCode单旁,默認(rèn)使用startActivityForResult啟動(dòng)Activity
.navigation(context, 123);
// 如果要打開的是fragment,需要使用
.createFragment();
3.3 路由表生成規(guī)則
如果兩條路由的path
饥伊、目標(biāo)className
完全相同象浑,則認(rèn)為是同一條路由,不會(huì)考慮參數(shù)是否相同琅豆。
路由表生成規(guī)則:編譯期按照如下順序取并集愉豺。
覆蓋規(guī)則:
根據(jù)如下順序,如果相同茫因,后者可以覆蓋前者的路由表規(guī)則蚪拦。
- 編譯期解析注解生成路由表
- 首先取
業(yè)務(wù)模塊 aar
中的路由表 - 再取 主
app module
代碼中的路由表 - 最后取
assets/RouteMap.json
文件中聲明的路由表。
- 如果編譯期沒有這個(gè)文件冻押,會(huì)生成一份默認(rèn)路由表放在這個(gè)目錄內(nèi)驰贷;如果有,會(huì)將路由表合并洛巢。
- 路由表生成時(shí)可配置是否啟用檢查路由合法性括袒,判斷目標(biāo)頁面是否存在,(warning/error)級(jí)別稿茉。
- 運(yùn)行時(shí)線上動(dòng)態(tài)下發(fā)的路由表
- 路由表允許線上動(dòng)態(tài)下發(fā)锹锰,將覆蓋本地路由表芥炭,詳見 【3.4 動(dòng)態(tài)路由表的設(shè)計(jì)與使用】
如果編譯期沒有這個(gè)文件,會(huì)生成一份默認(rèn)路由表放在這個(gè)目錄內(nèi)恃慧;如果有园蝠,會(huì)將路由表合并,因此糕伐,對(duì)于沒辦法修改代碼的第三方SDK內(nèi)部砰琢,如果希望通過路由打開,只需要手動(dòng)在RouteMap.json
文件中聲明良瞧,就能通過路由打開了陪汽。
3.4 動(dòng)態(tài)路由表的設(shè)計(jì)與使用
TheRouter
的路由表是動(dòng)態(tài)添加的,項(xiàng)目每次編譯后褥蚯,會(huì)在 apk 內(nèi)生成一份當(dāng)前 APP 的全量路由表挚冤,默認(rèn)路徑為:/assets/therouter/routeMap.json
。這個(gè)路由表也可以后續(xù)通過遠(yuǎn)程下發(fā)的方式使用赞庶,例如遠(yuǎn)端可以針對(duì)不同的APP版本训挡,下發(fā)不同的路由表達(dá)到配置目的。這樣如果將來線上某些頁面發(fā)生Crash歧强,可以通過將這個(gè)頁面的落地頁替換為H5的方式澜薄,臨時(shí)解決這類問題。
有兩種推薦的遠(yuǎn)程下發(fā)方式可供使用方選擇:
- 將打包系統(tǒng)與配置系統(tǒng)打通摊册,每次新版本APP打包后自動(dòng)將
assets/
目錄中的配置文件上傳到配置系統(tǒng)肤京,下發(fā)給對(duì)應(yīng)版本APP 。優(yōu)點(diǎn)在于全自動(dòng)不會(huì)出錯(cuò)茅特。 - 配置系統(tǒng)無法打通忘分,線上手動(dòng)下發(fā)需要修改的路由項(xiàng),因?yàn)?
TheRouter
會(huì)自動(dòng)用最新下發(fā)的路由項(xiàng)覆蓋包內(nèi)的路由項(xiàng)白修。優(yōu)點(diǎn)在于精確妒峦,且流量資源占用小。
注:一旦你設(shè)置了自定義的InitTask
兵睛,原框架內(nèi)路由表初始化任務(wù)將不再執(zhí)行肯骇,你需要自己處理找不到路由表時(shí)的兜底邏輯,一種建議的處理方式見如下代碼祖很。
// 此代碼 必須 在 Application.super.onCreate() 之前調(diào)用
RouteMap.setInitTask(new RouterMapInitTask() {
/**
* 此方法執(zhí)行在異步
*/
@Override
public void asyncInitRouteMap() {
// 此處為純業(yè)務(wù)邏輯累盗,每家公司遠(yuǎn)端配置方案可能都不一樣
// 不建議每次都請(qǐng)求網(wǎng)絡(luò),否則請(qǐng)求網(wǎng)絡(luò)的過程中突琳,路由表是空的若债,可能造成APP無法跳轉(zhuǎn)頁面
// 最好是優(yōu)先加載本地,然后開異步線程加載遠(yuǎn)端配置
String json = Connfig.doHttp("routeMap");
// 建議加一個(gè)判斷拆融,如果遠(yuǎn)端配置拉取失敗蠢琳,使用包內(nèi)配置做兜底方案啊终,否則可能造成路由表異常
if (!TextUtils.isEmpty(json)) {
List<RouteItem> list = new Gson().fromJson(json, new TypeToken<List<RouteItem>>() {
}.getType());
// 建議遠(yuǎn)端下發(fā)路由表差異部分,用遠(yuǎn)端包覆蓋本地更合理
RouteMap.addRouteMap(list);
} else {
// 在異步執(zhí)行TheRouter內(nèi)部兜底路由表
initRouteMap()
}
}
});
3.5 高級(jí)用法
TheRouter同時(shí)支持更多頁面跳轉(zhuǎn)能力傲须,詳情可參考項(xiàng)目文檔【https://github.com/HuolalaTech/hll-wp-therouter-android/wiki/Navigator】:
- 為第三方庫里面的頁面添加路由表蓝牲,達(dá)到對(duì)某些頁面降級(jí)替換的目的;
- 延遲路由跳轉(zhuǎn)(從Android 8開始泰讽,不能在后臺(tái)啟動(dòng)頁面)例衍;
- 跳轉(zhuǎn)過程攔截器(總共四層,可根據(jù)實(shí)際需求使用)已卸;
- 跳轉(zhuǎn)結(jié)果回調(diào)佛玄;
四、跨模塊依賴注入 ServiceProvider 的設(shè)計(jì)
對(duì)于模塊化開發(fā)中跨模塊的調(diào)用累澡,我們推薦采用 SOA(面向服務(wù)架構(gòu)) 的設(shè)計(jì)方式梦抢,服務(wù)調(diào)用方與使用方完全隔離,調(diào)用模塊外的能力不需要關(guān)注能力的提供者是誰愧哟。
ServiceProvider
的核心設(shè)計(jì)思想也是這樣的奥吩,目前服務(wù)間的調(diào)用協(xié)議采用接口的方式。當(dāng)然蕊梧,也可以兼容不通過接口下沉而是直接調(diào)用的情況霞赫。
具體到 Android 側(cè)就是 AIDL 類似的設(shè)計(jì),只是要比AIDL開發(fā)簡單很多:
- 服務(wù)提供方負(fù)責(zé)提供服務(wù)肥矢,不需要關(guān)心調(diào)用方是誰會(huì)在何時(shí)調(diào)用自己端衰。
- 服務(wù)的使用方只關(guān)注服務(wù)本身,不需要關(guān)心這個(gè)服務(wù)是誰提供的橄抹,只需要只能服務(wù)能提供哪些能力即可靴迫。
例如上面的圖片:拉拉需要使用錄音的服務(wù)惕味,小貨則向外提供一個(gè)錄音的服務(wù)楼誓,由TheRouter
的ServiceProvider
負(fù)責(zé)撮合。
4.1 服務(wù)使用方:拉拉
她無需關(guān)心名挥,IRecordService
這個(gè)接口服務(wù)是誰提供的疟羹,他只需要知道自己需要使用這樣的一個(gè)服務(wù)就行了。
注:如果沒有提供服務(wù)的提供方禀倔,TheRouter.get()
可能返回null
TheRouter.get(IRecordService::class.java)?.doRecord()
4.2 服務(wù)提供方:小貨
服務(wù)提供方需要聲明一個(gè)提供服務(wù)的方法榄融,用@ServiceProvider
注解標(biāo)記。
- 如果是
java
救湖,必須是public static
修飾 - 如果是
kotlin
愧杯,建議寫成 top level 的函數(shù) - 方法名不限
/**
* 方法名不限定,任意名字都行
* 返回值必須是服務(wù)接口名鞋既,如果是實(shí)現(xiàn)了服務(wù)的子類力九,需要加上returnType限定(例如下面代碼)
* 方法必須加上 public static 修飾耍铜,否則編譯期就會(huì)報(bào)錯(cuò)
*/
@ServiceProvider
public static IRecordService test() {
return new IRecordService() {
@Override
public void doRecord() {
String str = "執(zhí)行錄制邏輯";
}
};
}
// 也可以直接返回對(duì)象,然后標(biāo)注這個(gè)方法的服名是什么
@ServiceProvider(returnType = IRecordService.class)
public static RecordServiceImpl test() {
// xxx
}
五跌前、單模塊自動(dòng)初始化能力 FlowTaskExecutor 的設(shè)計(jì)
前面講過棕兼,TheRouter
是完全面向模塊化開發(fā)提供的一套解決方案。在模塊化開發(fā)時(shí)抵乓,可能每個(gè)模塊都有自己需要初始化的一些代碼伴挚。以前的做法是把這些代碼都在Application
里聲明,但是這樣可能隨著業(yè)務(wù)變動(dòng)每次都需要修改Application
所在模塊灾炭。TheRouter
的單模塊自動(dòng)初始化能力就是為了解決這樣的情況茎芋,可以只在當(dāng)前模塊聲明初始化方法后,將會(huì)在業(yè)務(wù)場景時(shí)自動(dòng)被調(diào)用咆贬。
每個(gè)希望被自動(dòng)初始化的方法败徊,必須使用public static
修飾,主要原因是這樣子就能通過類名直接調(diào)用了掏缎。另外很多初始化代碼都需要獲取Context
對(duì)象皱蹦,所以我們將Context
作為初始化方法的默認(rèn)參數(shù),會(huì)自動(dòng)傳入Application
眷蜈。其他的所在類名沪哺、方法名都沒有限制,反正只要加上了 @FlowTask
注解酌儒,在編譯期都能通過 APT 獲取到辜妓。
5.1 FlowTaskExecutor 使用介紹
可以在當(dāng)前模塊中,任意類中聲明一個(gè)任意方法名的方法忌怎,給方法添加上@FlowTask
的注解即可籍滴。
@FlowTask
注解參數(shù)說明:
-
taskName:當(dāng)前初始化任務(wù)的任務(wù)名,必須全局唯一榴啸,建議格式為:
moduleName_taskName
-
dependsOn:參考
Gradle
Task孽惰,任務(wù)與任務(wù)之間可能會(huì)有依賴關(guān)系。如果當(dāng)前任務(wù)需要依賴其他任務(wù)先初始化鸥印,則在這里聲明依賴的任務(wù)名勋功。可以同時(shí)依賴多個(gè)任務(wù)库说,用英文逗號(hào)分隔狂鞋,空格可選,會(huì)被過濾:dependsOn = "mmkv, config, login"潜的,默認(rèn)為空骚揍,應(yīng)用啟動(dòng)就被調(diào)用 - async:是否要在異步執(zhí)行此任務(wù),默認(rèn)false啰挪。
/**
* 將會(huì)在異步執(zhí)行
*/
@FlowTask(taskName = "mmkv_init", dependsOn = TheRouterFlowTask.APP_ONCREATE, async = true)
public static void test2(Context context) {
System.out.println("異步=========Application onCreate后執(zhí)行");
}
@FlowTask(taskName = "app1")
public static void test3(Context context) {
System.out.println("main線程=========應(yīng)用啟動(dòng)就會(huì)執(zhí)行");
}
/**
* 將會(huì)在主線程初始化
*/
@FlowTask(taskName = "test", dependsOn = "mmkv,app1")
public static void test3(Context context) {
System.out.println("main線程=========在app1和mmkv兩個(gè)任務(wù)都執(zhí)行以后才會(huì)被執(zhí)行");
}
5.2內(nèi)置初始化節(jié)點(diǎn)
使用這個(gè)能力信不,在路由內(nèi)部默認(rèn)支持了兩個(gè)生命周期類任務(wù)纤掸,可在使用時(shí)直接引用
- TheRouterFlowTask.APP_ONCREATE:當(dāng)Application的onCreate()執(zhí)行后初始化
- TheRouterFlowTask.APP_ONSPLASH:當(dāng)應(yīng)用的首個(gè)Activity.onCreate()執(zhí)行后初始化
同時(shí),使用TheRouter
的自動(dòng)初始化依賴浑塞,也無需擔(dān)心循環(huán)依賴造成的問題借跪,框架會(huì)在編譯期構(gòu)建有向無環(huán)圖,監(jiān)測循環(huán)依賴情況酌壕,如果發(fā)現(xiàn)會(huì)在編譯期直接報(bào)錯(cuò)掏愁,并且還會(huì)將發(fā)生循環(huán)引用的任務(wù)顯示出來,用于排錯(cuò)卵牍。
5.3 實(shí)現(xiàn)原理
每個(gè)加了 @FlowTask
注解的方法果港,都會(huì)在編譯期被解析,生成一個(gè)對(duì)應(yīng)的 Task
對(duì)象糊昙,這個(gè)對(duì)象包含了初始化方法的相關(guān)信息辛掠,比如:是否異步執(zhí)行、任務(wù)名释牺、是否依賴其他任務(wù)先執(zhí)行萝衩。
當(dāng)所有aar都編譯完成,生成好全部的 Task
以后没咙,會(huì)在主 app 中通過Gradle
插件進(jìn)行聚合猩谊,在這時(shí)會(huì)將所有的 Task
做一次檢查,通過構(gòu)建有向無環(huán)圖
來防止 Task
發(fā)生循環(huán)引用的情況祭刚。
每次應(yīng)用啟動(dòng)后牌捷,會(huì)在路由初始化時(shí),將有向圖中的全部Task
涡驮,按照依賴關(guān)系按順序加載暗甥。
六、動(dòng)態(tài)化能力 ActionManager 的設(shè)計(jì)
Action
本質(zhì)是一個(gè)全局的系統(tǒng)回調(diào)捉捅,主要用于預(yù)埋的一系列操作撤防,例如:彈窗、上傳日志锯梁、清理緩存即碗。
與 Android 系統(tǒng)自帶的廣播通知類似焰情,你可以在任何地方聲明動(dòng)作與處理方式陌凳。并且所有Action
都是可以被跟蹤的,只要你愿意内舟,可以在日志中將所有的動(dòng)作調(diào)用棧輸出合敦,以方便調(diào)試使用,這樣在一定程度上可以解決觀察者模式帶來的通惭橛巍:無法追蹤Observable
的問題充岛。
6.1 Action 使用
聲明一個(gè) Action:
// action建議遵循一定的格式
const val ACTION = "therouter://action/xxx"
@FlowTask(taskName="action_demo")
fun init(context: Context) =
TheRouter.addActionInterceptor(ACTION, object: ActionInterceptor() {
override fun handle(context: Context, args: Bundle): Boolean {
// do something
return false
}
})
執(zhí)行一個(gè) Action:
// action建議遵循一定的格式
const val ACTION = "therouter://action/xxx"
// 如果執(zhí)行了一個(gè)沒有被聲明的Action保檐,則不會(huì)有任何動(dòng)作
TheRouter.build(ACTION).action()
6.2 高級(jí)用法
每個(gè)Action
允許關(guān)聯(lián)多個(gè) ActionInterceptor
進(jìn)行處理,多個(gè)ActionInterceptor
之間可以自定義攔截器優(yōu)先級(jí)崔梗,同時(shí)允許終止接下來的低優(yōu)先級(jí)攔截器的執(zhí)行夜只。
最典型應(yīng)用場景:首頁可能會(huì)有多個(gè)彈窗,不同業(yè)務(wù)之間的彈窗是有優(yōu)先級(jí)之分的蒜魄,為了體驗(yàn)優(yōu)化我們肯定不會(huì)在首頁一次把所有彈窗全部彈出扔亥,可以通過ActionInterceptor
為每個(gè)彈窗聲明好優(yōu)先級(jí)關(guān)系,假設(shè)需求是首頁只能彈出3個(gè)彈窗谈为,那么第三個(gè)彈窗處理完畢即可關(guān)閉當(dāng)前事件旅挤,接下來的攔截器將不會(huì)被響應(yīng)。
abstract class ActionInterceptor {
abstract fun handle(context: Context, args: Bundle): Boolean
fun onFinish() {}
/**
* 數(shù)字越大伞鲫,優(yōu)先級(jí)越高
*/
open val priority: Int
get() = 5
}
6.3 客戶端動(dòng)態(tài)響應(yīng)使用場景
如果僅客戶端使用粘茄,常用的場景可能是:當(dāng)用戶執(zhí)行某些操作(打開某個(gè)頁面、H5點(diǎn)擊某個(gè)按鈕秕脓、動(dòng)態(tài)頁面配置的點(diǎn)擊事件)時(shí)柒瓣,將會(huì)自動(dòng)觸發(fā),執(zhí)行預(yù)埋的 Action 邏輯吠架。
如果與服務(wù)端鏈路打通嘹朗,這個(gè)能力其實(shí)是需要整個(gè)公司的配合,比如有一套類似智慧大腦的方案诵肛,可以基于客戶端過去的一些埋點(diǎn)數(shù)據(jù)屹培,智能推斷出用戶下一步要做的事情,然后通過長連接直接向客戶端下發(fā)指令做某些事情怔檩。那么通過客戶端預(yù)埋的頁面跳轉(zhuǎn)褪秀、彈窗、清緩存薛训、退出登錄等等操作媒吗,就可以通過服務(wù)端指令進(jìn)行操作,則就是一套完整的動(dòng)態(tài)化方案乙埃。
七闸英、一鍵切換源碼與 AAR
7.1 模塊化支持的 Gradle 腳本
在模塊化開發(fā)過程中,如果沒有采用分倉介袜,或采用了分倉但依然使用 git-submodule
的方式開發(fā)甫何,應(yīng)該都會(huì)遇到一個(gè)問題。如果集成包采用源碼編譯遇伞,構(gòu)建時(shí)間實(shí)在太久辙喂,大大降低開發(fā)調(diào)試效率;如果采用aar依賴編譯,對(duì)于底層模塊修改了代碼巍耗,每次都要重新構(gòu)建aar秋麸,在上層模塊修改版本號(hào)以后,才能繼續(xù)整包構(gòu)建編譯炬太,也極大影響開發(fā)效率灸蟆。
TheRouter
中提供了一個(gè) Gradle
腳本,只需要在開發(fā)本地的local.properties
文件中聲明要參與編譯的module
亲族,其他未聲明的默認(rèn)使用aar編譯次乓,這樣就能靈活切換源碼與aar,并且不會(huì)影響其他人孽水,如下節(jié)選代碼可供參考使用:
/**
* 如果工程中有源碼票腰,則依賴源碼,否則依賴aar
*/
def moduleApi(String compileStr, Closure configureClosure) {
String[] temp = compileStr.split(":")
String group = temp[0]
String artifactid = temp[1]
String version = temp[2]
Set<String> includeModule = new HashSet<>()
rootProject.getAllprojects().each {
if (it != rootProject) includeModule.add(it.name)
}
if (includeModule.contains(artifactid)) {
println(project.name + "源碼依賴:===project(\":$artifactid\")")
projects.project.dependencies.add("api", project(':' + artifactid), configureClosure)
// projects.project.configurations { compile.exclude group: group, module: artifactid }
} else {
println(project.name + "依賴:=======$group:$artifactid:$version")
projects.project.dependencies.add("api", "$group:$artifactid:$version", configureClosure)
}
}
在實(shí)際使用時(shí)女气,可以完全使用moduleApi
替換掉原有的api
杏慰。當(dāng)然, implementation
也可以有一個(gè)對(duì)應(yīng)的moduleImplementation
炼鞠,這樣只需要注釋或解注釋setting.gradle
文件內(nèi)的include
語句就可以達(dá)到切換源碼缘滥、aar
的目的了。
八谒主、從其他路由遷移至 TheRouter
8.1 遷移工具一鍵遷移
TheRouter
提供了圖形化界面的遷移工具朝扼,可以一鍵從其他路由遷移到TheRouter
,目前僅支持ARouter
霎肯,其他路由框架遷移也在開發(fā)中(GitHub下載擎颖,70M左右,請(qǐng)耐心等待):
- Mac OS 遷移工具下載:https://github.com/HuolalaTech/hll-wp-therouter-android/wiki/uploads/file/TheRouterTransfer-Mac.zip
- Windows 遷移工具下載:https://github.com/HuolalaTech/hll-wp-therouter-android/wiki/uploads/file/TheRouterTransfer-Windows.zip
如果項(xiàng)目中使用了ARouter的IProvider.init()方法观游,可能需要手動(dòng)處理初始化邏輯搂捧。
如下圖:
8.2 與其他路由對(duì)比
功能 | TheRouter | ARouter | WMRouter |
---|---|---|---|
Fragment路由 | ?? | ?? | ?? |
支持依賴注入 | ?? | ?? | ?? |
加載路由表 | 無運(yùn)行時(shí)掃描 無反射 |
運(yùn)行時(shí)掃描dex 反射實(shí)例類 性能損耗大 |
運(yùn)行時(shí)讀文件 反射實(shí)例類 性能損耗中 |
注解正則表達(dá)式 | ?? | ?? | ?? |
Activity指定攔截器 | ??(四大攔截器可根據(jù)業(yè)務(wù)定制) | ?? | ?? |
導(dǎo)出路由文檔 | ??(路由文檔支持添加注釋描述) | ?? | ?? |
動(dòng)態(tài)注冊(cè)路由信息 | ?? | ?? | ?? |
APT支持增量編譯 | ?? | ??(開啟文檔生成則無法增量編譯) | ?? |
plugin支持增量編譯 | ?? | ?? | ?? |
多 Path 對(duì)應(yīng)同一頁面(低成本實(shí)現(xiàn)雙端path統(tǒng)一) | ?? | ?? | ?? |
遠(yuǎn)端路由表下發(fā) | ?? | ?? | ?? |
支持單模塊獨(dú)立初始化 | ?? | ?? | ?? |
支持使用路由打開第三方庫頁面 | ?? | ?? | ?? |
支持使用路由打開第三方庫頁面 | ?? | ?? | ?? |
對(duì)熱修復(fù)支持(例如tinker) | ??(未改變的代碼多次構(gòu)建無變動(dòng)) | ??(多次構(gòu)建apt產(chǎn)物會(huì)發(fā)生變化,生成無意義補(bǔ)丁) | ??(多次構(gòu)建apt產(chǎn)物會(huì)發(fā)生變化懂缕,生成無意義補(bǔ)丁) |
九允跑、總結(jié)
TheRouter
并不僅僅是一個(gè)小巧靈活的路由庫,而是一整套完整的 Android
模塊化解決方案搪柑,能夠解決幾乎全部的模塊化過程中會(huì)遇到的問題聋丝。
對(duì)于現(xiàn)有的路由框架,我們也在最大限度支持平滑遷移工碾,目前已完成ARouter
的一鍵遷移工具弱睦,其他框架的遷移仍在開發(fā)中。你也可以在Github
issue
中提出需求倚喂,我們?cè)u(píng)估后會(huì)盡快支持每篷,也歡迎任何人提供 Pull Requests
。
更多問題請(qǐng)?jiān)L問:詳細(xì)溝通