iOS 動態化的故事

發表於2016-12-22

今天聊聊 iOS 動態化的故事。

問題

在開發模式上,web 的方式是比較先進的,有各種優點,包括跨平臺/UI開發效率高,最重要的是可以時刻保證使用者看到的程式是最新的,沒有版本概念,整個系統時刻保持在掌握之中,而客戶端開發模式相對 web 開發是一種倒退,客戶端做不到這樣的動態化,無法隨時更新,目前一個客戶端程式要更新成本是很高的,需要釋出版本,也無法保證所有人都能更新到這個版本,這是最大的弱點,也是非常大的一塊需求。

原因

為什麼會有這種倒退,最主要原因是:蘋果引領的體驗優先規則。

在 iPhone 出現之前大家並不太在意一個軟體的動畫體驗,一個 web 應用是很少有動畫的,點一個按鈕,一整塊內容直接重新整理,再點個連結整個頁面變白重新整理,PC上網頁滾動都是一格格滾動的,而不是現在手機上那種順滑流暢的滾動,PC客戶端軟體也一樣,大家都覺得沒什麼問題,用得挺好,但蘋果改變了這種情況,iPhone 剛推出時頁面間切換的動畫,60fps 的絲滑滾動,點選的即時響應,微軟的人都驚呼是黑魔法,讓人用了就上癮,再也回不去,而 web 的方式還不足以像原生客戶端那樣支援這樣的流暢性,做不到好的體驗,無法被人接受,開發上優勢再多也無法幹過客戶端,參考 facebook 初期用 web 技術構建 app 的慘狀,沒辦法,服了蘋果,大家只能按照蘋果的方式幹,做原生客戶端。

當然這裡還有手機環境網路不穩定,流量費貴的原因,但這些都可以在技術上通過快取解決,最主要還是體驗問題。

那發展到今天,這個體驗問題解決了沒有呢?沒有,即使發展到今天手機效能已上天,但 web 做出來的東西體驗仍然跟客戶端有差別,大家也已經習慣了 APP 的方式,也被流暢的 APP 慣壞。現在 APP 裡也有不少功能是 web實現,但大家都知道這是犧牲了一些效能體驗去換取開發和釋出效率,只是一種權衡。

解決方案

那這個問題怎麼解決呢,現在業界有兩種方案。

1.優化 web 效能

既然用 web 方式開發的劣勢只是效能體驗跟不上,那就優化效能吧。web 效能瓶頸在哪裡?在那個有悠久歷史的 webkit 引擎,它有各種歷史問題,要改進它並不容易。我們要的是 web 的開發和釋出方式,不需要 web 的全部,那能不能重新實現一個渲染引擎呢,這個引擎可以針對原生客戶端優化,不需要相容繁雜的 web 標準,不跟 web 那些歷史問題扯上關係,於是就有了 React NativeWeex 這種方案,web 的方式開發,原生的方式渲染,擁有 web 優秀的開發和釋出方式,又有不錯的效能體驗,看起來很完美,很有前途的方案。

一個方案能不能推廣開讓大家都使用,主要看成本和收益。目前 React Native 和 Weex 等這些方案的接入成本是很高的,一是它們本身就是大型框架,學習成本高,後期維護成本以及團隊學習成本同樣高。二是它們還不夠成熟,還在繼續填坑中,使用的過程可能要一起去填坑。收益上也不夠理想,就目前狀況它們能代替的是那些本來用 H5 實現的模組,換成這種方式實現後效能體驗會更好,但也不能保證像原生那樣好,很多場景需要深入框架進行優化。整個 APP 都使用這種方案構建還不靠譜,部分使用又無法使整個 APP 保持動態化,總體上來說收益也沒有達到有絕對優勢的程度,成本高收益低,推廣起來會比較困難,還需期待其繼續發展。

2.原生動態化

另一種是方案是,我可以放棄用 web 的開發方式,放棄 web 跨平臺/UI開發簡單的優越性,我想繼續用原生的方式開發這個APP,但又希望這個 APP 隨時可以更新,讓程式時刻在自己掌握中,出了問題可以快速修復,還想要可以隨時更新版本快速迭代,可以不?完全可以,用動態庫就行了。

動態庫

技術上要在 iOS 上做到原生動態化比 Android 更容易,iOS 開發語言 Objective-C 天生動態,執行時都能隨意替換方法,執行時載入動態庫又是項很老的技術,只要我把增量的程式碼和資源打包到一個 framework 裡,動態下發執行時載入,修 bug,加功能都不在話下,效能完全無損,這件事就結束了。

但是呢。蘋果把載入動態庫的功能給封了,動態庫必須跟隨安裝包一起簽名才能被載入,無法通過別的途徑簽名後再下發。

為什麼這麼做呢,這又涉及到蘋果另一個創舉:稽核模式(蘋果的創舉之多令人髮指)。一個軟體,要在一個平臺上釋出,需要先通過這個平臺的人工稽核,這個好像在蘋果之前沒見過有別人這樣做過,windows 不用,mac 不用,各種 unix 不用,web 也不用,蘋果為了對自己的平臺有絕對控制權,搞了這樣一個東西,稽核模式就跟動態化衝突了,如果我一個 APP 可以不經過稽核不斷下發 framework 新增修改功能, 還需要蘋果稽核做什麼?

因為這種限制,沒法用最方便的方式進行動態更新了,整個 APP 發出去後就不受控制,有什麼嚴重 bug,需要新增什麼功能,都乖乖打個包提交給蘋果稽核,再等使用者慢慢更新,對於急性子的中國人來說,這種事難以容忍。

繞道

蘋果把動態庫這扇門關了,我們可以繞個道從另一個門進,動態加功能可以緩緩,大家需求還不那麼迫切,但修 bug 的需求就很急,很多公司 APP 的 crash 率是 KPI 來著,看著線上 crash 不斷增多又毫無辦法是很不爽的事,於是有了 waxPatchJSPatch 這種方案,曲線救國。

JSPatch 把 OC 手動翻譯成 JS,在執行時通過 OC 的動態特性去呼叫和替換 OC 方法,實時修復 bug。修 bug 這個需求基本是滿足了,雖然小繞了下道,但成本還是很低的,引擎本身也很小很輕量,接入對 APP 不會有任何負面影響,在關鍵時刻又可以幫大忙,成本低收益高,於是很容易推廣開。

人慾望是無窮的,技術宅的折騰是無止境的,JSPatch 滿足了修 bug 的需求,但還是無法滿足動態化的全部需求,最大的缺點在於需要手寫 JS,雖然已經有轉換器輔助,但還沒做到100%準確,用來修 bug 還好,用來新增功能的話學習成本和開發效率還不夠。

於是有了滴滴的 DynamicCocoa 這種方案,繞了一個更大的道,從編譯階段入手,通過 clang 把 OC 程式碼編譯成自己定製的 JS 格式,再動態下發去執行,做到原生開發,動態執行,主打動態新增功能,當然順便把修 bug 也給支援了。手機 QQ 內部也有一個類似的方案,不過更進一步,他們通過 clang 把 OC 程式碼編譯成自己定製的位元組碼動態下發,然後開發一個虛擬機器去執行(驚呆了),同樣實現了原生開發,動態執行,都是 NB 得很的方案。只要底層處理做得足夠好,也是個成本低收益高的方案,不過目前都還沒開源,還沒能看到實際效果和 NB 的原始碼,挺期待。

稽核

這種方案有沒有什麼問題呢,問題在對於蘋果稽核比較尷尬。這種方案做到極致後(所有OC/C語法都支援),實際上是繞道實現了動態載入 framework 的全部功能,開發體驗還是一致的,如果蘋果同意這種方案,那相當於允許載入動態庫,那還不如直接把門開了,讓大家直接用動態庫去做這個事情,用動態庫還能在簽名時禁止使用私有 API,用這種編譯成指令碼/位元組碼下發的方案可就禁止不了了。

這跟 JSPatch 還不太一樣,JSPatch 雖然我也想推廣動態新增功能的用法,但因為開發體驗問題大部分還是用於修 bug,蘋果稽核對 JSPatch 開始也是有一些拒絕案例,後來估計看到大家只是用它來修 bug 和 crash,提升 APP 的質量,就默許了。但 DynamicCocoa 和手Q的方案一開始目標和效果就是跟載入動態庫對齊,大規模推廣後蘋果會怎麼看就不知道了。

我覺得蘋果現在的稽核方式挺有問題的,有多少APP是稽核時用一套,稽核通過後又通過後臺一些開關把不符合規則的一些功能開放出來?只要能連上網,就有N種方式修改 APP 裡的功能,蘋果完全攔不住。個人認為稽核方式應該改為只在釋出新 APP 時稽核,釋出後允許動態下發程式碼更新,版本更新也不需要重新稽核,而是通過舉報和抽查的方式去稽核已上線的 APP,這樣既能顧及開發效率,方便開發者快速迭代做出更好的 APP,也更能確保稽核效果,只是實施起來沒有現在簡單粗暴。

最後

故事講到這裡已經差不多了,再多說一點,有個讓我覺得很奇怪的問題,就是國外開發者只熱衷於使用第一種方案解決問題,也就是使用web技術,用 React Native / NativeScript 的方式去做這個事情,對第二種方案很冷淡,包括 iOS Android 都是,原生熱更新只在國內火,國外根本不感冒,國外有個 rollout 熱更新平臺也不溫不火,為什麼呢?是國外使用者更守規則,或是使用者對線上 bug 容忍度高,開發者對線上 bug 並不那麼著急?還是 Android 被 Google Play 卡死斷了這念想,iOS JSPatch 之類的方案推廣不利?缺少國外一線開發者的支援,讓系統原生支援動態化就比較困難了。

個人認為由系統支援動態化(允許載入動態庫)在當前環境下是最好的,兼顧開發效率和 APP 體驗,雖然不能跨平臺,但也還能接受,可惜這個主動權掌握在蘋果手上,開發者無能為力,才會出現這麼多強行繞道突破的方案。web 的方式可能是未來,但目前適用範圍有限,接下來怎麼發展,拭目以待吧。

相關文章