你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> iOS 應用架構談 動態部署方案

iOS 應用架構談 動態部署方案

編輯:關於IOS

iOS 應用架構談 動態部署方案

  • iOS應用架構談 開篇

  • iOS應用架構談 view層的組織和調用方案

  • iOS應用架構談 網絡層設計方案

  • iOS應用架構談 動態部署方案

  • iOS應用架構談 本地持久化方案

前言

這裡討論的動態部署方案,就是指通過不發版的方式,將新的內容、新的業務流程部署進已發布的App。因為蘋果的審核周期比較長,而且蘋果的限制比較多,業界在這裡也沒有特別多的手段來達到動態部署方案的目的。這篇文章主要的目的就是給大家列舉一下目前業界做動態部署的手段,以及其對應的優缺點。然後給出一套我比較傾向於使用的方案。

其實單純就動態部署方案來講,沒什麼太多花頭可以說的,就是H5、Lua、JS、OC/Swift這幾門基本技術的各種組合排列。寫到後面覺得,動態部署方案其實是非常好的用於講解某些架構模式的背景。一般我們經驗總結下來的架構模式包括但不限於:

  1. Layered Architecture

  2. Event-Driven Architecture

  3. Microkernel Architecture

  4. Microservices Architecture

  5. Space-Based Architecture

我在開篇裡面提到的MVC等方案跟這篇文章中要提到的架構模式並不是屬於同一個維度的。比較容易混淆的就是容易把MVC這些方案跟Layered Architecture混淆,這個我在開篇這篇文章裡面也做過了區分:MVC等方案比較側重於數據流動方向的控制和數據流的管理。Layered Architecture更加側重於各分層之間的功能劃分和模塊協作。

另外,上述五種架構模式在Software Architecture Patterns這本書裡有非常詳細的介紹,整本書才45頁,個把小時就看完了,非常值得看和思考。本文後半篇涉及的架構模式是以上架構模式的其中兩種:Microkernel Architecture和Microservices Architecture。

最後,文末還給出了其他一些關於架構模式的我覺得還不錯的PPT和論文,裡面對架構模式的分類和總結也比較多樣,跟Software Architecture Patterns的總結也有些許不一樣的地方,可以博采眾長。

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,比如貼紙。

架構模式

其實我們要做到動態部署,至少要滿足以下需求:

  1. View和事件都要能夠動態部署

  2. 功能完整

  3. 便於維護

我更加傾向於H5和Native以JSBridge的方式連接的方案進行動態部署,在cocoapods裡面也有蠻多的JSBridge了。看了一圈之後,我還是選擇寫了一個CTJSBridge,來滿足動態部署和後續維護的需求。關於這個JSBridge的使用中的任何問題和需求,都可以在評論區向我提出來。接下來的內容,會主要討論以下這些問題:

  1. 為什麼不是React-Native或其它方案?

  2. 采用什麼樣的架構模式才是使用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的微服務架構模式

我開發了一個,基於JSBridge的微服務架構差不多是這樣的:

                                 -------------------------                                  |                       |                                  |         HTML5         |                                  |                       |                                  | View + Event Response |                                  |                       |                                  -------------------------                                              |                                              |                                              |                                           JSBridge                                              |                                              |                                              |         ------------------------------------------------------------------------------         |                                                                            |         |   Native                                                                   |         |                                                                            |         |  ------------   ------------   ------------   ------------   ------------  |         |  |          |   |          |   |          |   |          |   |          |  |         |  | Service1 |   | Service2 |   | Service3 |   | Service4 |   |    ...   |  |         |  |          |   |          |   |          |   |          |   |          |  |         |  ------------   ------------   ------------   ------------   ------------  |         |                                                                            |         |                                                                            |         ------------------------------------------------------------------------------

解釋一下這種架構背後的思想:

因為H5和Native之間能夠通過JSBridge進行交互,然而JSBridge的一個特征是,只能H5主動發起調用。所以理所應當地,被調用者為調用者提供服務。

另外一個想要處理的問題是,希望能夠通過微服務架構,來把H5和Native各自的問題域區分開。所謂區分問題域就是讓H5要解決的問題和Native要解決的問題之間,交集最小。因此,我們設計時希望H5的問題域能夠更加偏重業務,然後Native為H5的業務提供基礎功能支持,例如API的跨域調用,傳感器設備信息以及本地已經沉澱的業務模塊都可以作為Native提供的服務交給H5去使用。H5的快速部署特性特別適合做重業務的事情,Native對iPhone的功能調用能力和控制能力特別適合將其封裝成服務交給H5調用。

所以這對Native提供的服務有兩點要求:

  1. Native提供的服務不應當是強業務相關的,最好是跟業務無關,這樣才能方便H5進行業務的組裝

  2. 如果Native一定要提供強業務相關的服務,那最好是一個完整業務,這樣H5就能比較方便地調用業務模塊。

只要Native提供的服務符合上述兩個條件,HTML5在實現業務的時候,束縛就會非常少,也非常容易管理。

然後這種方案也會有一定的局限性,就是如果Native沒有提供這樣的服務,那還是必須得靠發版來解決。等於就是Native向HTML5提供API,這其實跟服務端向Native提供API的道理一樣。

但基於Native提供的服務的通用性這點來看,添加服務的需求不會特別頻繁,每一個App都有屬於自己的業務領域,在同一個業務領域下,其實需要Native提供的服務是有限的。然後結合JSPatch提供的動態patch的能力,這樣的架構能夠滿足絕大部分動態部署的需求。

然後隨著App的不斷迭代,某些HTML5的實現其實是可以逐步沉澱為Native實現的,這在一定程度上,降低了App早期的試錯成本。

基於動態庫的微內核模式

我開發了CTDynamicLibKit這個庫來解決動態庫的調用問題,其實原先的打算是拿動態庫做動態部署的,不過我用@念紀 的個人App把這個功能塞進去之後,發現蘋果還是能審核通過的,但是下載下來的動態庫是無法加載的。報錯如下:

error:Error Domain=NSCocoaErrorDomain Code=3587 "The bundle “DynamicLibDemo” couldn’t be loaded because it is damaged or missing necessary resources." (dlopen_preflight(/var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo): no suitable image found.  Did find:         /var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo: code signature invalid for '/var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo'     ) UserInfo=0x174260b80 {NSLocalizedFailureReason=The bundle is damaged or missing necessary resources., NSLocalizedRecoverySuggestion=Try reinstalling the bundle., NSFilePath=/var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo, NSDebugDescription=dlopen_preflight(/var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo): no suitable image found.  Did find:         /var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo: code signature invalid for '/var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework/DynamicLibDemo'     , NSBundlePath=/var/mobile/Containers/Data/Application/61D3BF00-CF8D-4157-A87C-D999905E9040/Library/DynamicLibDemo1.framework, NSLocalizedDescription=The bundle “DynamicLibDemo” couldn’t be loaded because it is damaged or missing necessary resources.}

主要原因是因為簽名無法通過。因為Distribution的App只能加載相同證書打包的framework。在in house和develop模式下,可以使用相同證書既打包App又打包framework,所以測試的時候沒有問題。但是在正式的distribution下,這種做法是行不通的。

所以就目前看來,基於動態庫的動態部署方案是沒辦法做到的。

總結

我在文中針對業界常見的動態部署方案做了一些總結,並且提供了我自己認為的最佳解決方案以及對應的JSBridge實現。文中提到的方案我已經盡可能地做到了全面,如果還有什麼我遺漏沒寫的,大家可以在評論區指出,我把它補上去。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved