0 引言
時代演進(jìn)国旷,技術(shù)也隨之發(fā)展匿级。到今天,APP已然成為絕大多數(shù)互聯(lián)網(wǎng)企業(yè)用來獲取用戶的核心渠道椭豫。與此同時贩耐,伴隨著業(yè)務(wù)量的增長弧腥,愈來愈大、愈來愈多的APP也在不斷地潮太、持續(xù)地挑戰(zhàn)著每一個移動端研發(fā)人員的知識深度管搪,而我們的移動端技術(shù)人員也在這個不斷接受挑戰(zhàn)的過程中,成就了今天的移動互聯(lián)網(wǎng)時代铡买。餓了么移動APP就是這樣一個挑戰(zhàn)更鲁,多用戶量、多業(yè)務(wù)量奇钞,在接受著更多更挑剔用戶的同時澡为,默默地、不斷地演進(jìn)著移動端的架構(gòu)蛇券。
1 MVC
我們常說缀壤,脫離業(yè)務(wù)談架構(gòu)就是純粹的刷流氓樊拓。餓了么移動APP的發(fā)展也是其業(yè)務(wù)發(fā)展的一面鏡子。
在餓了么業(yè)務(wù)發(fā)展的早期塘慕,移動APP經(jīng)歷從無到有的階段筋夏。為了快速上線搶占市場,傳統(tǒng)移動APP開發(fā)的MVC架構(gòu)成了“短平快”思路的首選:
這種架構(gòu)以層次結(jié)構(gòu)簡單清晰图呢,代碼容易開發(fā)而被大多數(shù)人所接受条篷。
在MVC的體系架構(gòu)中,Controller層負(fù)責(zé)整個APP中主要邏輯功能的實現(xiàn)蛤织;Model層則負(fù)責(zé)數(shù)據(jù)結(jié)構(gòu)的描述以及數(shù)據(jù)持久化的功能赴叹;而View層作為展現(xiàn)層負(fù)責(zé)渲染整個APP的UI。分工清晰指蚜,簡潔明了乞巧;并且這種系統(tǒng)架構(gòu)在語言框架層就得到了Apple的支持,所以非常適用于APP的startup開發(fā)摊鸡。
然后绽媒,這種架構(gòu)在開發(fā)的后期會由于其超高耦和性,從而造就龐大Controller層免猾,而這也是一直被人所詬病是辕。最終的MVC都從Model-View-Controller走向了Massive-View-Controller 的終點。
2 Module Decoupled
“短平快”的MVC架構(gòu)幫助餓了么移動APP快速搶占了市場猎提。而隨著代碼量的不斷增加获三,臃腫的Controller層也在漸露頭角;而業(yè)務(wù)上锨苏,餓了么移動APP也從單一APP發(fā)展為多APP齊頭并進(jìn)的格局疙教。這時候,如果降低耦合蚓炬,復(fù)用已有模塊成了架構(gòu)的第一要務(wù)松逊。
架構(gòu)中,模塊復(fù)用的第一要求便是代碼的功能組件化肯夏。組件化意味著擁有獨立功能的代碼從系統(tǒng)中進(jìn)行抽象并剝離经宏,再以“插件”的形式插回原有系統(tǒng)中。這樣剝離出來的功能組件驯击,便可以供其他APP進(jìn)行使用烁兰,從而降低系統(tǒng)中模塊與模塊之間的耦和性;也同時提高了APP之間代碼的復(fù)用性徊都。
餓了么移動對于組件有兩種定義:公有組件和業(yè)務(wù)組件沪斟。公有組件指的是封裝得比較好的一些SDK,包括一些第三方組件和自己內(nèi)部使用的組件。如iOS中最著名的網(wǎng)絡(luò)SDK AFNetworking主之,Android下OKHttp择吊,都是這類組件的代表。而對于業(yè)務(wù)組件槽奕,則定義為包含了一系列業(yè)務(wù)功能的整體几睛,例如登錄業(yè)務(wù)組件,注冊業(yè)務(wù)組件粤攒,即為此類組件的典型代表所森。
對于公有組件,餓了么移動采取了版本化的管理方式夯接,而這在iOS和Android平臺上也早有比較成熟的解決方案焕济。例如,對于iOS平臺盔几,CocoaPods基本上成為了代碼組件化管理的標(biāo)配晴弃;在Android平臺上,Gradle也是非常成熟和穩(wěn)健的方案问欠。采用以上管理工具的另一個原因在于肝匆,對企業(yè)開發(fā)而言,代碼也是一種商業(yè)機(jī)密顺献。基于保密性的目的枯怖,支持內(nèi)網(wǎng)搭建私有服務(wù)器成為了必需注整。以上的管理工具都能夠很好地支持這些操作。
對于業(yè)務(wù)的組件化度硝,我們采取了業(yè)務(wù)模塊注冊機(jī)制的方式來達(dá)到解耦合的目的肿轨。每個業(yè)務(wù)模塊對外提供相應(yīng)的業(yè)務(wù)接口,同時在系統(tǒng)啟動的時候向Excalibur系統(tǒng)注冊自己模塊的Scheme(Excalibur是餓了么移動用來保存Scheme與模塊之間映射的系統(tǒng)蕊程,同時能根據(jù)Scheme進(jìn)行Class反射返回)椒袍。 當(dāng)其他業(yè)務(wù)模塊對該業(yè)務(wù)模塊有依賴時,從Excalibur系統(tǒng)中獲取相關(guān)實例藻茂,并調(diào)用相應(yīng)接口來實現(xiàn)調(diào)用驹暑,從而實現(xiàn)了業(yè)務(wù)模塊之間的解耦目的。
而在業(yè)務(wù)組件辨赐,即業(yè)務(wù)模塊的內(nèi)部优俘,則可以根據(jù)不同開發(fā)人員的偏好,來實現(xiàn)不同的代碼架構(gòu)掀序。如現(xiàn)在討論得比較火的MVVM, MVP等帆焕,都可以在模塊內(nèi)部進(jìn)行而不影響整體系統(tǒng)架構(gòu)。
這時候的架構(gòu)看起來更像是這樣:
這種E(Excalibur)M(Modules)C(Common)架構(gòu)以高內(nèi)聚不恭、低耦合為主要的特點叶雹,以面向接口編程為出發(fā)點财饥,降低了模塊與模塊之間的聯(lián)系。
該架構(gòu)的另外一大好處則在于解決了不同系統(tǒng)版本的兼容性問題折晦。這里舉iOS平臺下的WebView作為例子來進(jìn)行說明佑力。Apple從iOS8系統(tǒng)開始提供了一套更好的Web支持框架——WebKit,但在iOS7系統(tǒng)下卻無法兼容筋遭,從而導(dǎo)致Crash打颤。使用此類架構(gòu),可以在iOS7系統(tǒng)下仍然注冊使用傳統(tǒng)的WebView來渲染網(wǎng)頁漓滔,而在iOS8及其以上系統(tǒng)注冊WebKit來作為渲染網(wǎng)頁的內(nèi)核编饺。即避免了Apple嚴(yán)格的審核機(jī)制,又達(dá)到了動態(tài)加載的目的响驴。
3 Hybrid
移動APP的開發(fā)有兩種不同的路線透且,Native APP和Web APP。這兩種路線的區(qū)別類似于PC時代開發(fā)應(yīng)用程序時的C/S架構(gòu)和 B/S架構(gòu)豁鲤。
以上我們談到的都屬于典型的Native APP秽誊,即所有的程序都由本地組件渲染完成。這類APP優(yōu)點是顯而易見的琳骡,渲染速度快锅论、用戶體驗好;缺點同時也十分突出:出現(xiàn)了錯誤一定要等待下一次用戶進(jìn)行APP更新才能夠修復(fù)楣号。
Web APP的優(yōu)點恰好就是Native APP的缺點所在最易,其頁面全部采用H5撰寫并存放在服務(wù)器端。每次進(jìn)行頁面渲染時都從服務(wù)器請求最新的頁面炫狱。一旦頁面有錯誤服務(wù)器端進(jìn)行更新便能立刻解決藻懒。不過其弊端也容易窺見:每次頁面都需要請求服務(wù)器,造成渲染時等待時間過長视译,從而導(dǎo)致的用戶體驗不夠完美嬉荆,并且性能上較Native APP慢了1-2個數(shù)量級;與此同時還會導(dǎo)致更多的用戶流量消耗酷含。另一個缺點則在于鄙早,Web APP在移動端上調(diào)用本地的硬件設(shè)備存在一定的不便。不過這些弊端也都有相應(yīng)的解決方案第美,如PhoneGap將網(wǎng)頁提前打包在本地以減少網(wǎng)絡(luò)的請求時間蝶锋;同時也提供一系列的插件來訪問本地的硬件設(shè)備。然而什往,盡管如此扳缕,其渲染速度上還是會稍微存在一定的差距。
Hybrid APP則是綜合了二者優(yōu)缺點的解決方案。餓了么移動對于此二類APP的觀點在于躯舔,純粹展示性的模塊會更適合使用Web頁面來達(dá)到渲染的目的驴剔;而更多的數(shù)據(jù)操作性、動畫渲染性的模塊則更適合采用Native的方式粥庄。
基于之前的EMC架構(gòu)丧失,我們將部分模塊重新進(jìn)行了架構(gòu):
Hybrid-EMC架構(gòu)中,Web作為一個子模塊惜互,注冊加入到整個系統(tǒng)中布讹,從而實現(xiàn)讓業(yè)務(wù)上需要快速迭代的模塊達(dá)到實時更新的效果。
4 React-Native & Hot Patch
經(jīng)過這些年的業(yè)務(wù)發(fā)展训堆,Hybrid提供的展示界面更新方案也逐漸地?zé)o法滿足APP更新迭代的需要描验。因此越來越多的動態(tài)部署的方案被提了出來,比如iOS下的JSPatch, waxPatch坑鱼,Android下的Dexpose,AndFix, ClassLoader膘流,都是比較成熟Hot Patch動態(tài)部署解決方案。這些方案的思路都是通過下載遠(yuǎn)程服務(wù)器的代碼來動態(tài)更新本地的代碼行為鲁沥。
React-Native則屬于另一種動態(tài)部署的方案呼股,其核心原理在于通過JavaScript來調(diào)用本地組件進(jìn)行界面的渲染。
而餓了么移動APP發(fā)展到今天画恰,各個APP綜合用戶量已經(jīng)過億彭谁。因此一個非常小的Bug所帶來的問題都可能會直接影響到幾萬人的使用。為了保證APP的穩(wěn)定性和健壯性阐枣,Hot Patch方案也就成了當(dāng)下最有待解決的問題马靠。
根據(jù)80%的用戶訪問20%頁面這一80/20原則,保證這20%訪問最頻繁的頁面的穩(wěn)定性就是保證了80%的APP的穩(wěn)定性蔼两。因此,餓了么移動對于部分訪問最頻繁的模塊進(jìn)行了React-Native備份逞度。當(dāng)這部分頁面出現(xiàn)問題時额划,APP可以通過服務(wù)器的配置,自動切換成React-Native的備份頁面档泽;而與此同時開發(fā)人員開發(fā)一個小而精的Hot Patch來修復(fù)出現(xiàn)的問題俊戳。當(dāng)Hot Patch完成修補后,再切換回Native APP的原生功能馆匿。
這時候的架構(gòu)看起來會像是這樣:
HotPatch-EMC的架構(gòu)主要目標(biāo)在于解決移動APP的穩(wěn)定性問題抑胎。通過RN與Native的主備,可以減少系統(tǒng)APP出錯帶來的失誤成本渐北。
5 結(jié)語
我們都知道阿逃,對于軟件工程來說,這世上沒有銀彈。對于架構(gòu)而言其實也非常的適用恃锉。業(yè)務(wù)的不斷更新搀菩,帶來了餓了么移動APP架構(gòu)的不斷演進(jìn)。
架構(gòu)沒有真正的好壞之分破托,只要適用于自己的業(yè)務(wù)肪跋,就是好的架構(gòu)!