iOS應用架構談動態部署方案
2015年12月13日
32
分類:iOS APP開發方案
這里討論的動態部署方案,就是指通過不發版的方式,將新的內容、新的業務流程部署進已發布的App。因為蘋果的審核周期比較長,而且蘋果的限制比較 多,業界在這里也沒有特別多的手段來達到動態部署方案的目的。這篇文章主要的目的就是給大家列舉一下目前業界做動態部署的手段,以及其對應的優缺點。然后 給出一套我比較傾向于使用的方案。
Web App
實現方案
其實所謂的web app,就是通過手機上的瀏覽器進行訪問的H5頁面。這個H5頁面是針對移動場景特別優化的,比如UI交互等。
優點
無需走蘋果流程,所有蘋果流程帶來的成本都能避免,包括審核周期、證書成本等。
版本更新跟網頁一樣,隨時生效。
不需要Native App工程師的參與,而且市面上已經有很多針對這種場景的框架。
缺點
由于每一頁都需要從服務器下載,因此web app重度依賴網絡環境。
同樣的UI效果使用web app來實現的話,流暢度不如Native,比較影響用戶體驗。
本地持久化的部分很難做好,繞過本地持久化的部分的辦法就是提供賬戶體系,對應賬戶的持久化數據全部存在服務端。
即時響應方案、遠程通知實現方案、移動端傳感器的使用方案復雜,維護難度大。
安全問題,H5頁面等于是所有東西都暴露給了用戶,如果對安全要求比較高的,很多額外的安全機制都需要在服務端實現。
總結
web app一般是創業初期會重點考慮的方案,因為迭代非常快,而且創業初期的主要目標是需要驗證模式的正確性,并不在于提供非常好的用戶體驗,只需要完成閉環 即可。早年facebook曾經嘗試過這種方案,最后因為用戶體驗的問題而宣布放棄。所以這個方案只能作為過渡方案,或者當App不可用時,作為降級方案 使用。
Hybrid App
通過市面上各種Hybrid框架,來做H5和Native的混合應用,或者通過JS Bridge來做到H5和Native之間的數據互通。
優點
除了要承擔蘋果流程導致的成本以外,具備所有web app的優勢
能夠訪問本地數據、設備傳感器等
缺點
跟web app一樣存在過度依賴網絡環境的問題
用戶體驗也很難做到很好
安全性問題依舊存在
大規模的數據交互很難實現,例如圖片在本地處理后,將圖片傳遞給H5
總結
Hybrid方案更加適合跟本地資源交互不是很多,然后主要以內容展示為主的App。在天貓App中,大量地采用了JS Bridge的方式來讓H5跟Native做交互,因為天貓App是一個以內容展示為主的App,且營銷活動多,周期短,比較適合Hybrid。
React-Native
嚴格來說,React-Native應當放到Hybrid那一節去講,單獨拎出來的原因是Facebook自從放出React-Native之后,業界討論得非常激烈。天貓的鬼道也做了非常多的關于React-Native的分享。
React-Native這個框架比較特殊,它展示View的方式依然是Native的View,然后也是可以通過URL的方式來動態生成 View。而且,React-Native也提供了一個Bridge通道來做Javascript和Objective-C之間的交流,還是很貼心的。
然而研究了一下發現有一個比較坑的地方在于,解析JS要生成View時所需要的View,是要本地能夠提供的。舉個例子,比如你要有一個特定的 Mapview,并且要響應對應的delegate方法,在React-Native的環境下,你需要先在Native提供這個Mapview,并且自己 實現這些delegate方法,在實現完方法之后通過Bridge把數據回傳給JS端,然后重新渲染。
在這種情況下我們就能發現,其實React-Native在使用View的時候,這些View是要經過本地定制的,并且將相關方法通過RCT_EXPORT_METHOD
暴露給js,js端才能正常使用。在我看來,這里在一定程度上限制了動態部署時的靈活性,比如我們需要在某個點擊事件中展示一個動畫或者一個全新的view,由于本地沒有實現這個事件或沒有這個view,React-Native就顯得捉襟見肘。
優點
響應速度很快,只比Native慢一點,比webview快很多。
能夠做到一定程度上的動態部署
缺點
組裝頁面的元素需要Native提供支持,一定程度上限制了動態部署的靈活性。
總結
由于React-Native框架中,因為View的展示和View的事件響應分屬于不同的端,展示部分的描述在JS端,響應事件的監聽和描述都在 Native端,通過Native轉發給JS端。所以,從做動態部署的角度上講,React-Native只能動態部署新View,不能動態部署新 View對應的事件。當然,React-Native本身提供了很多基礎組件,然而這個問題仍然還是會限制動態部署的靈活性。因為我們在動態部署的時候, 大部分情況下是希望View和事件響應一起改變的。
另外一個問題就在于,View的原型需要從Native中取,這個問題相較于上面一個問題倒是顯得不那么嚴重,只是以后某個頁面需要添加某個復雜的view的時候,需要從現有的組件中拼裝罷了。
所以,React-Native事實上解決的是如何不使用Objc/Swift來寫iOS App的View
的問題,對于如何通過不發版來給已發版的App更新功能
這樣的問題,幫助有限。
Lua Patch
大眾點評的屠毅敏同學在基于wax的基礎上寫了waxPatch,這個工具的主要原理是通過lua來針對objc的方法進行替換,由于lua本身是解釋型語言,可以通過動態下載得到,因此具備了一定的動態部署能力。然而iOS系統原生并不提供lua的解釋庫,所以需要在打包時把lua的解釋庫編譯進app。
優點
能夠通過下載腳本替換方法的方式,修改本地App的行為。
執行效率較高
缺點
對于替換功能來說,lua是很不錯的選擇。但如果要添加新內容,實際操作會很復雜
很容易改錯,小問題變成大問題
總結
lua的解決方案在一定程度上解決了動態部署的問題。實際操作時,一般不使用它來做新功能的動態部署,主要還是用于修復bug時代碼的動態部署。實際操作時需要注意的另外一點是,真的很容易改錯,尤其是你那個方法特別長的時候,所以改了之后要徹底回歸測試一次。
Javascript Patch
這個工作原理其實跟上面說的lua那套方案的工作原理一樣,只不過是用javascript實現。而且最近新出了一個JSPatch這個庫,相當好用。
優點
同Lua方案的優點
打包時不用將解釋器也編譯進去,iOS自帶JavaScript的解釋器,只不過要從iOS7.0以后才支持。
缺點
同Lua方案的缺點
總結
在對app打補丁的方案中,目前我更傾向于使用JSPatch的方案,在能夠完成Lua做到的所有事情的同時,還不用編一個JS解釋器進去,而且會javascript的人比會lua的人多,技術儲備比較好做。
JSON Descripted View
其實這個方案的原理是這樣的:使用JSON來描述一個View應該有哪些元素,以及元素的位置,以及相關的屬性,比如背景色,圓角等等。然后本地有一個解釋器來把JSON描述的View生成出來。
這跟React-Native有點兒像,一個是JS轉Native,一個是JSON轉Native。但是同樣有的問題就是事件處理的問題,在事件處 理上,React-Native做得相對更好。因為JSON不能夠描述事件邏輯,所以JSON生成的View所需要的事件處理都必須要本地事先掛好。
優點
能夠自由生成View并動態部署
缺點
天貓實際使用下來,發現還是存在一定的性能問題,不夠快
事件需要本地事先寫好,無法動態部署事件
總結
其實JSON描述的View比React-Native的View有個好處就在于對于這個View而言,不需要本地也有一套對應的View,它可以 依據JSON的描述來自己生成。然而對于事件的處理是它的硬傷,所以JSON描述View的方案,一般比較適用于換膚,或者固定事件不同樣式的View, 比如貼紙。
我的選擇
其實我們要做到動態部署,至少要滿足以下需求:
View和事件都要能夠動態部署
功能完整
便于維護
我更加傾向于H5和Native以JSBridge的方式連接的方案進行動態部署,在cocoapods里面也有蠻多的JSBridge了。看了一圈之后,我還是選擇寫了一個CTJSBridge,來滿足動態部署和后續維護的需求。關于這個JSBridge的使用中的任何問題和需求,都可以在評論區向我提出來。接下來的內容,會主要討論以下這些問題:
為什么不是React-Native或其它方案?
采用什么樣的架構模式才是使用JSBridge的最佳實踐?
為什么不是React-Native或其他方案?
首先針對React-Native來做解釋,前面已經分析到,React-Native有一個比較大的局限在于View需要本地提供。假設有一個頁
面的組件是跑馬燈,如果本地沒有對應的View,使用React-Native就顯得很麻煩。然而同樣的情況下,HTML5能夠很好地實現這樣的需求。這
里存在一個這樣的取舍在性能和動態部署View及事件之間,選擇哪一個?
我更加傾向于能夠動態部署View和事件
,至少后者是能夠完成需求的,性能再好,難以完成需求其實沒什么意義。然而對于
HTML5的Hybrid和純HTML5的web
app之間,也存在一個相同的取舍,但是還要額外考慮一個新的問題,純HTML5能夠使用到的設備提供的功能相對有限,JSBridge能夠將部分設備的
功能以Native API的方式交付給頁面,因此在考慮這個問題之后,選擇HTML5的Hybrid方案就顯得理所應當了。
在諸多Hybrid方案中,除了JSBridge之外,其它的方案都顯得相對過于沉重,對于動態部署來說,其實需要補充的軟肋就是提供本地設備的功能,其它的反而顯得較為累贅。
采用什么樣的架構模式才是使用JSBridge的最佳實踐?
微服務的架構模式是我認為的最佳實踐。微服務的方式是這樣的:
------------------------- | | | HTML5 | | | | View + Event Response | | | ------------------------- | | | JSBridge | | | ------------------------------------------------------------------------------ | | | Native | | | | ------------ ------------ ------------ ------------ ------------ | | | | | | | | | | | | | | | Service1 | | Service2 | | Service3 | | Service4 | | ... | | | | | | | | | | | | | | | ------------ ------------ ------------ ------------ ------------ | | | | | ------------------------------------------------------------------------------
解釋一下這樣的架構:Native通過JSBridge向HTML5提供了各種各樣的服務,包括但不限于:數據持久化、喚起Native某業務流程 (例如拍照、圖片上傳)、定時器、跨域或不跨域的API請求、數據同步等。然而Native不應該提供強業務相關的服務,強業務應當由HTML5負責完 成,HTML5在完成業務的過程中會根據需要調度到Native提供的Service。
所以,只要Native提供了足夠充分的服務,HTML5在實現業務的時候,完全可以做到自由揮灑。
然后這種方案也會有一定的局限性,就是如果Native沒有提供這樣的服務,那還是必須得靠發版來解決。等于就是Native向HTML5提供API,跟服務端向Native提供API的道理一樣。
但基于Native提供的服務的通用性這點來看,添加服務的需求不會特別頻繁,每一個App都有屬于自己的業務領域,在同一個業務領域下,其實需要 Native提供的服務是有限的。然后結合JSPatch提供的動態patch的能力,這樣的架構能夠滿足絕大部分動態部署的需求。
然后隨著App的不斷迭代,某些HTML5的實現其實是可以逐步沉淀為Native實現的,這在一定程度上,降低了App早期的試錯成本。