客戶端動(dòng)態(tài)化系列之——URLRoute

對(duì)于客戶端來說,發(fā)版本身就屬于一種很高成本的行為。然而一個(gè)初創(chuàng)的app,會(huì)有各式各樣的問題,而在初期也不會(huì)像大型app一般有一套成熟的處理異常機(jī)制。而這往往會(huì)造成許多問題,那么問題來了,如何在有限的開發(fā)資源下,做到客戶端的動(dòng)態(tài)化。并且實(shí)現(xiàn)降級(jí)、ABTest等等一系列的行為呢?

What we want?

如前文提到,當(dāng)版本迭代時(shí),首當(dāng)其沖的就是版本的問題。作為團(tuán)隊(duì)的開發(fā),我曾經(jīng)花了很多時(shí)間在思考一個(gè)問題。如何能讓我們的架構(gòu)模型變得優(yōu)雅,變得像橡皮泥一般能隨著業(yè)務(wù)的發(fā)展迅速變形,特別是在一個(gè)創(chuàng)業(yè)公司,迫切需要一個(gè)強(qiáng)大的架構(gòu)體系能支撐業(yè)務(wù)的快速發(fā)展。

那么事情就變得很簡(jiǎn)單了,在維持業(yè)務(wù)發(fā)展速度不變的情況下,盡可能減少發(fā)版的頻率,以技術(shù)的方式在不發(fā)版的情況下滿足“偽發(fā)版”。

當(dāng)然,還有其他的一些想法,會(huì)在后文提到:

  • ABTest
  • 灰度
  • 降級(jí)策略
  • 動(dòng)態(tài)調(diào)用本地服務(wù)

What to do?

有了想法知道,思路就比較清晰了,會(huì)針對(duì)性的對(duì)目的進(jìn)行處理。

“偽發(fā)版”

對(duì)于app來說,偽發(fā)版的方案已經(jīng)在業(yè)內(nèi)比較成熟了。我們可以用weex/react native(以同一JS代碼,在H5、iOS、Android同時(shí)實(shí)現(xiàn)同一頁(yè)面)的方式來實(shí)現(xiàn)在不發(fā)版的情況下實(shí)現(xiàn)頁(yè)面級(jí)別的更新。

我們?cè)囅胍幌拢m然weex實(shí)現(xiàn)了頁(yè)面的動(dòng)態(tài)更新。但是卻沒法很優(yōu)雅地處理新頁(yè)面與老頁(yè)面的跳轉(zhuǎn)關(guān)系。打個(gè)比方,一個(gè)native頁(yè)面A已經(jīng)在代碼里寫死點(diǎn)擊按鈕跳轉(zhuǎn)頁(yè)面B,此時(shí)就算用weex/rn寫了一個(gè)新頁(yè)面,也是很難跳轉(zhuǎn)的(當(dāng)然如果兩個(gè)頁(yè)面都是weex/rn寫的,就不會(huì)存在這樣的問題,但是在大多數(shù)應(yīng)用場(chǎng)景里,還是會(huì)存在部分weex/rn,部分native的頁(yè)面)。

即 A -> B 是一個(gè)既定的關(guān)系。為了達(dá)到動(dòng)態(tài)化,必然需要對(duì)整個(gè)app的導(dǎo)航進(jìn)行統(tǒng)一處理。
這就是URLRoute要做的事情!

正如其名,URLRoute會(huì)有一個(gè)路由規(guī)則

URLRoute

而動(dòng)態(tài)化的秘訣就是將無法變動(dòng)的邏輯代碼維護(hù)成文本。即 代碼 -> 文本 。

文本沒有編譯、簽名等等發(fā)版需要的步驟,它的更新完全是可以由文件的更新完成,而這恰恰是不需要發(fā)版的。

拿iOS為例,我實(shí)現(xiàn)了一個(gè)URLRoute的庫(kù),其中的處理流程可以由下圖所示:

其中URL與對(duì)應(yīng)的class的映射是由一個(gè)文件維護(hù)的,如圖所示:

通過維護(hù)一個(gè)dictionary(map)維護(hù)URL與本地頁(yè)面或服務(wù)的關(guān)系。

ABTest && 灰度

任何業(yè)務(wù)在發(fā)展的階段都不可避免會(huì)產(chǎn)生分歧,此時(shí)需要如果業(yè)務(wù)架構(gòu)支持對(duì)不同的人群命中不同粒度的產(chǎn)品,并且給出數(shù)據(jù)以展示哪一種結(jié)果更好,這對(duì)于思考產(chǎn)品的人有更多的選擇余地。

而通過URLRoute,我們完全可以針對(duì)不同的人群派發(fā)不同的映射文件達(dá)到ABTest的目的。

甚至在某一個(gè)版本上可以做到灰度,10%->20%->...->100%。

降級(jí)策略

試想一下,如果以上線的項(xiàng)目產(chǎn)生了重大BUG怎么辦。對(duì)于iOS來說,可以通過Runtime的方式達(dá)到patch。但是對(duì)于Android來說,現(xiàn)有的三種方案:classloader,更改jar包,更改apk。后兩者更不用說,我們并不是一個(gè)apk。而前者在現(xiàn)如今的動(dòng)態(tài)方案中逐漸被拋棄。其實(shí)都不適合我們,這些其實(shí)都需要一套完整的解決方案,并不僅僅是導(dǎo)入庫(kù)就行,還要考慮與server的交互等。

那么如何做到一個(gè)方式,能以最簡(jiǎn)單的方式做到在產(chǎn)生BUG時(shí),以最小的成本并且在兩端以同樣的策略去做到修復(fù)或者降級(jí)呢?

答案是我們可以通過更改映射文件,將原先有問題的URL對(duì)應(yīng)的value替換掉,即A->B變成A->C(C可以是一個(gè)error頁(yè)面,也可以是一個(gè)weex/rn頁(yè)面)。當(dāng)然你會(huì)問如果本地沒有實(shí)現(xiàn)C怎么辦?沒關(guān)系,我們刪掉那條URL記錄,當(dāng)URLRoute攔截不到URL時(shí),會(huì)以webview的形式打開,這樣就降級(jí)成H5了

動(dòng)態(tài)調(diào)用本地服務(wù)

URLRoute不僅僅處理了頁(yè)面之間的跳轉(zhuǎn)邏輯,也可以處理service。這里的service可以理解為任何native的行為,比如登錄、強(qiáng)制更新、發(fā)網(wǎng)絡(luò)請(qǐng)求等等

我們可以事先注冊(cè)好對(duì)應(yīng)的module,通過URL調(diào)用對(duì)應(yīng)的service

這是很重要的,比如我們產(chǎn)生了重大BUG,我們就可以強(qiáng)制登出用戶,甚至強(qiáng)制所有頁(yè)面都跳到error頁(yè)面等等

How to do?

我們可以在要實(shí)現(xiàn)URLRoute的class,在plist文件里聲明,key為對(duì)應(yīng)的host + path,value為對(duì)應(yīng)的class name

// 默認(rèn)調(diào)用方式
OpenURL(@"http://m.kuailejim.com/home?id=552131");
// 如果需要傳URL不能表達(dá)的參數(shù)(Object參數(shù))
OpenURLWithParams(@"http://m.kuailejim.com/home?id=552131", paramsDictionary);

這里的scheme為http,是因?yàn)槲以赨RLRoute里做了處理,當(dāng)這里的key(m.kuailejim.com/home)沒有被攔截到即在plist文件中并沒有找到對(duì)應(yīng)的class來處理時(shí),我會(huì)判斷scheme是否為http/https,如果是,則會(huì)跳到H5以實(shí)現(xiàn)降級(jí)

我們知道,URL傳參對(duì)于object類型是不友好的,于是需要實(shí)現(xiàn)一個(gè)能帶dictionary(map)的方法,并且在URLRoute內(nèi)部實(shí)現(xiàn)基于key/value形式參數(shù)的解析。

對(duì)應(yīng)的class需要實(shí)現(xiàn)下述方法,這樣URLRoute通過runtime動(dòng)態(tài)創(chuàng)建對(duì)應(yīng)的class,并且調(diào)用class已經(jīng)實(shí)現(xiàn)的代理方法達(dá)到處理的效果:

- (id)initWithUrlRequest:(URLRouteRequest *)request;

這里還需要注意,在URLRoute里要hook對(duì)應(yīng)的viewdidappear方法來拿到當(dāng)前的view controller,才能做到push和model的操作。如果是model操作還需實(shí)現(xiàn)一個(gè)navigation controller(因?yàn)橛锌赡軙?huì)在新的頁(yè)面里push)

AOP統(tǒng)一處理,例如想在跳轉(zhuǎn)時(shí)對(duì)所有url記錄埋點(diǎn)等功能,這個(gè)很好理解,只要在URLRoute里在實(shí)現(xiàn)攔截前統(tǒng)一做處理就行

總結(jié)

這只是客戶端動(dòng)態(tài)化的第一步,baby steps to big dreams

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,869評(píng)論 18 139
  • 前言 隨著用戶的需求越來越多,對(duì)App的用戶體驗(yàn)也變的要求越來越高。為了更好的應(yīng)對(duì)各種需求,開發(fā)人員從軟件工程的角...
    一縷殤流化隱半邊冰霜閱讀 87,422評(píng)論 214 1,098
  • 在前端越來越火的年代,逐漸衍生出類似React Native、Weex等開發(fā)套件。所達(dá)到的目的挺簡(jiǎn)單的,達(dá)到在多個(gè)...
    kuailejim閱讀 4,430評(píng)論 2 36
  • 原文鏈接:https://github.com/halfrost/Halfrost-Field/blob/mast...
    hament閱讀 5,702評(píng)論 1 31
  • 最是人間惆帳客, 寂寞無處是柔鄉(xiāng)。 紅燭錦帳倆人傾, 云山巫雨已秋實(shí)。
    忘情水1987閱讀 289評(píng)論 0 0