背景
隨著有貨App的業(yè)務(wù)不斷迭代,功能不斷累積,原有的項目架構(gòu)逐漸出現(xiàn)了以下問題:業(yè)務(wù)模塊代碼邊界不清晰驶臊,耦合過重;業(yè)務(wù)代碼與通用代碼的耦合導(dǎo)致很多重復(fù)代碼叼丑;基礎(chǔ)組件庫的不完善導(dǎo)致了一些三方庫的重復(fù)关翎。鑒于此,組件化勢在必行鸠信,一個簡易的架構(gòu)分層圖如下:
單向依賴纵寝,業(yè)務(wù)組件相互之間無依賴關(guān)系,那業(yè)務(wù)組件之間的頁面跳轉(zhuǎn)該如何解決呢症副?
路由
現(xiàn)在比較通用的一個解決辦法是采用路由店雅,現(xiàn)在社區(qū)有很多開源路由框架,比如ARouter贞铣,DeepLinkDispatch闹啦,ActivityRouter,LiteRouter等辕坝,再結(jié)合有貨現(xiàn)有的業(yè)務(wù)跟組件化架構(gòu)窍奋,基于以下幾點實現(xiàn)一個路由方案:
注解配置,通過APT在編譯時去生成各個組件的路由表
AspectJ去匯總各個組件的路由表,同樣通過APT生成輔助代碼實現(xiàn)
路由調(diào)用支持原本Bundle支持的各種數(shù)據(jù)類型琳袄,各種Activity跳轉(zhuǎn)的Flags江场,跳轉(zhuǎn)動畫以及支持startActivityForResult
支持?jǐn)r截器,特別是先跳轉(zhuǎn)登錄再跳轉(zhuǎn)請求的頁面這種異步的場景窖逗,可以通過RxJava的Subject實現(xiàn)
Activity的參數(shù)自動注入址否,類似ButterKnife,這個優(yōu)先級不高
路由后的結(jié)果回調(diào)
兼容原本App中的老跳轉(zhuǎn)規(guī)則碎紊,可以將原來的流程對接到新的路由sdk
簡單用一個流程圖表示一下佑附,下面會具體講:
實踐
對于上面說的幾點,我從路由使用的角度來詳細(xì)闡述一下仗考。
- 首先整個路由sdk有三部分組成
· router是一些代碼中用到的API
· router-annotation定義了一些需要用到的注解
· router-processor定義了注解處理器
· 初始化
在Application的onCreate()里初始化路由sdk音同,所做的事情很簡單,只是初始化一個List秃嗜,這個List負(fù)責(zé)匯總各個組件模塊中的路由权均。
- 路由定義
· 這個注解會在編譯時由注解處理器來解析
· 此注解支持配置多個url
· url的格式:scheme://host/path/{paramKey},url正則匹配锅锨,為了不使url的解析過于復(fù)雜叽赊,這里的param匹配只會解析成String類型,對于其余的類型則可通過Router API手動添加
· url匹配舉例:yoho://detail/88
除了Router這個注解還需要一個RouterModule注解:
· 這個注解同樣在編譯時由注解處理器來解析橡类,注解一個空類即可蛇尚,它會生成一個類DetailModuleRouter,這個類生成在當(dāng)前組件的包名下顾画,用來匯總該組件下所有的Router注解并放入一個List中用于匹配
· 除此之外取劫,注解處理器還需要在DetailModuleRouter中插入一段AspectJ調(diào)用代碼,用于匯總各個組件模塊路由:
簡單解釋一下研侣,就是將該組件的路由module類加到一個匯總所有路由module類的List中谱邪,這個List就是上面第二步所說的初始化的List,加入的地方與時機便是由AspectJ來決定庶诡,這段AspectJ注解的意思就是切入路由sdk的初始化方法惦银,在調(diào)用該方法后的代碼中去執(zhí)行該組件的路由匯總工作,其他組件同理末誓。這樣做主要是因為組件化后各個業(yè)務(wù)組件編寫代碼期間無依賴關(guān)系扯俱,只有編譯運行后才可見([參照得到的組件化方案]),也就不能強引用各個組件的路由module類喇澡,而AspectJ則是在編譯期間AOP正好可以應(yīng)對這個情況迅栅,由于項目中之前做其他功能已經(jīng)引入了AspectJ,所以對于我們來說不是很重的選擇晴玖。
· 我們看下AspectJ后的字節(jié)碼:
-
編譯期所做的事情基本理完读存,下面就是正式調(diào)用Router相關(guān)的API为流,一個例子如下:
RouterCall代表了一次路由操作,用Builder模式去構(gòu)造让簿,有很多putXxx方法敬察,可以去添加Bundle支持的各種數(shù)據(jù)類型,Activity相關(guān)的Flags(如Intent.FLAG_ACTIVITY_NEW_TASK)尔当,Activity直接跳轉(zhuǎn)的動畫等莲祸,這些數(shù)據(jù)都封裝在RouterMap這個類中,然后支持添加攔截器和routerCallback椭迎,這兒的攔截器是同步的虫给,異步的后面會說,route有一個重載的帶requestCode的方法侠碧,如果帶入一個大于0的值,則會去調(diào)用startActivityForResult缠黍。攔截器調(diào)用鏈參考了okHttp的實現(xiàn)弄兜,真正的路由跳轉(zhuǎn)操作也當(dāng)成一個攔截器,放到所有攔截器的最后瓷式。下面是這個過程的一個簡單時序圖:
- 異步攔截器
2· 并沒有放到路由sdk中替饿,因為跟業(yè)務(wù)有一些耦合
3· 我這里用RxJava的PublishSubject來實現(xiàn),Subject屬于Hot Observable贸典,有點類似EventBus视卢,但是可以當(dāng)成一個專用的EventBus
4· 一個簡單例子如下:
如果需要登錄則先路由到登錄,登錄成功后發(fā)射一下:sUserPublishSubject.onNext(sUser)則會跳轉(zhuǎn)到原先的路由頁面廊驼,要注意及時的dispose据过,否則界面會亂跳。
總結(jié)
上面列出了路由實踐中重要的一些點妒挎,還有些未實現(xiàn)比如支持方法調(diào)用绳锅,安全考慮,會逐步完善酝掩。為方便開發(fā)鳞芙,形成一份路由url映射的文檔很有必要,這個也可以在編譯時去生成期虾,可以參考DeepLinkDispatch原朝,不再贅述。
參考文獻(xiàn)
感謝社區(qū)優(yōu)秀的路由庫和相關(guān)文章:
· http://www.reibang.com/p/8a3eeeaf01e8