美團大眾點評 Hybrid 化建設
導語
上周末,精神哥去參加了好友小青在北京辦的T沙龍,探討移動端熱更新相關的話題。Bugly曾為大家介紹過不少騰訊內部的熱更新的框架,正好這次看到了美團,去哪兒以及微博同學在應用熱更新方面的實踐,整理出來發給大家,本周整理的是美團大眾點評的吳卓同學分享的分享的 美團 Hybrid 化建設,其他的內容也會在后面陸續放出。
Hybrid 是移動端熱更新最常用的手段,限于 App Store 上架審核時間較長,美團大眾點評也采取了該方案,歡迎來自美團大眾點旅游業務 iOS 負責人 吳卓 分享 《美團大眾點評 酒旅方面 Hybrid 化建設》 。
首先自我介紹一下:
大家好!我是吳卓,很高興能來到 T 沙龍做這個分享,今天我將從 iOS 的角度跟大家一起探討一下美團點評整體在 Hybrid 建設中做一些事情。
我進入比較早,在 2011 年的 7 月份最早在美團實習。后來又繼續出國讀研,同時做一名兼職的開發者,在 2013 年的時候,做過 iOS 的獨立開發,有很多人把它作為自己的一項事業去做。
后來在 2014 年 12 月份重新加入美團,現在是旅游 iOS 的負責人。
我負責的主要是住宿,度假,大交通,整個業務部門成立時間是相對比較晚,像住宿只做了三年,度假做了兩年,大交通是去年才開始做的。快速迭代的能夠給業務一個非常好的支持。
今天的內容主要分成四個部分:
-
第一簡單介紹一下為什么我們要做一個 Hybrid 化這樣一個東西。
-
第二部分是今天的重點部分,會講一下我們在 Hybrid 化上做的一些事情。
-
第三部分會簡單回顧一下,我們做的一些內容和對現有的一些方案做一些對比。
-
最后,如果大家有問題,可以做一些交流。
一、為什么做 Hybrid 化?
第一個問題,我們為什么要做 Hybrid 這個東西,其實剛剛提到整個業務發展非常迅速。在迅速發展中,我們直接面臨了以下兩個非常棘手的問題:
1. 客戶端發版周期長
第一個問題客戶端發版周期比較長,相信大家應該有類似的感受,特別是在一個大公司里面,迭代是相對固定的周期。另外在 iOS 里面如果需要發版還需要 App Store 的審核。
2. 前端資源嚴重不足
第二個問題是我們公司一個現狀,前端資源嚴重不足。
解決方案
首先,針對第一個問題,客戶端發版周期長,我們希望通過一些手段脫離客戶端發版限制。
至于第二個問題,我們希望把現有的前端和客戶端的同學完全結合起來,共同開發我們主要的一個 APP 。
二、Hybrid 化設計
接下來講一下我們 Hybrid 化整體的設計,總體上我們是用一種 Native 和 H5 頁面強混合的模式 。
如果在美團上買一個火車票,我不知道有沒有同學買過。其實在美團上買一張火車票,有一部分是 Native 頁面,有一些是 H5 頁面,有一部分組件是 Native 做的,有一部分組件是 H5 做的。
如果使用這種方式做的話,我們會遇到以下三個問題:
-
H5 和 Native 上線時間不一致,如何銜接?
-
H5 和 Native 之間如何進行通信?
-
H5 頁面如何接近 Native 的體驗?
1. H5 和 Native 上線時間不一致,如何銜接?
第一個問題是說現在的頁面里面既有 H5 頁面,也有 Native 頁面,Native 頁面在 App Store 上面的, H5 相對比較靈活的。
所以有個問題,當H5上線之后,客戶端需要給H5提供一些跳轉的入口,這個跳轉的入口提供的應該是在不發版的情況下去給出的,能夠通過這種靈活的配置去實現 H5 到 Native 的一個過渡。
美團 APP 現狀
我們來講一下美團 APP 的現狀,早在 2014 年美團 APP 其實大部分頁面是由 Native 編寫的,只有一些活動的展示頁面,是用 H5 形式的頁面展示的。
為了實現頁面之間的解耦合,每個頁面其實會有一個 URL 進行標識,根據每次跳轉到一個 Native 頁面,現在很多公司都采用類似的方式去做。現在是這樣的模式,那怎樣讓 Native 頁面過渡到 H5 呢?
我們的方案是對這個跳轉去做一些擴展。本質上來說,客戶端這邊是從 URL 到 Native 頁面的路由表,我們想辦法對跳轉的參數做的一些擴展,讓他能夠支持跳轉到 H5 里面,甚至跳轉到 URL 的頁面。
上圖的這個配置能夠通過后臺進行下發,進行同時的更新,同時為了做這個更新,我們也為這個路由配置做了一個前端的展示頁面。整體來說通過我們在原有的這種跳轉模式下做了一些動態化的擴展,實現后續客戶端發版之后能夠從后臺下發一些配置。
舉一個簡單的例子:
在美團 APP 買一個團購的訂單,用戶需要訪問列表頁,商家的詳情頁,創建訂單,最后購買成功。
如果我們有一些新版本的上線沒辦法支持展示這些新的產品,對一個新的產品做一個 H5 的產品詳情和創立訂單頁面,把這個產品切換到走 H5 的流程最終的客戶端發版走這種 H5 的流程。
這樣無論是新的用戶還有沒有升級的老的用戶,都及時的訪問到我們最新的產品。
小結
在這兒簡單回顧一下,我們做這個事情的一些設計思路。
剛才說的配置下發只是在特殊情況下做的,因為這種情況是少數,不會每天所有的頁面都做這種事情,所以我們并不會下發整個客戶端里面的所有的配置,我們只是把一些需要更新的內容做一些回應,從后臺下發下去。
另外一點是說上層的使用方,我們內部會幫上層調用方,做好所有的相關的工作。
2. H5 和 Native 之間如何進行通信?
第二個問題,前端的 H5 頁面和 Native 頁面怎么更新,因為他兩個是完全不同語言開發的,其實這個方案的話我們一般來說, 把 Native 和 H5 的通信機制約定為,稱之為橋協議 。
這個橋協議,它是一個雙向的通信方式。綠色部分是講 NativeJS ,這個是比較直觀的,在 WEB 應用里面直接可以調用這個方法。
JS 調用 Native
但是 JS 調用 Native 的方法其實系統沒有提供一個很直接的方法,這個地方其實是我們需要解決的一個問題。
基本上, JS 調用 Native 本質上就是,給客戶端去傳遞一些消息,傳遞的消息格式其實是比較隨意的,而且時間只要約定好就可以了。
現在問題就是怎么去傳?
這個問題,我們當時在做的時候,其實調研了一下常規的方案來分析。有三個方案,我具體說一下:
-
第一個方案是通過 URL 攔截的方法
這個什么意思呢?就是說,對于前端來說如果 JS 需要給應用傳消息,一般會開一個 Server ,會訪問一個地址,這個地址他的 Scheme 是一個特殊的 Scheme 。客戶端這邊會攔截到這種指令格式的 URL 需求,實現一個 JS 到 Native 傳遞消息的一個過程。
-
第二個方案叫主動輪詢
對于 JS 他需要把給 Native 傳遞的消息,轉化成一個 JSON ,客戶端這邊一般會開一個線程,每隔一段時間會調 JS 的方法,從這個方法里面把 JS 需要給 Native 傳遞的消息全部取出來,取出來之后再去做相應的操作。
-
第三個就是 JSContext
前端可以直接調用客戶端本地的方法。
我們簡單對比一下這三種方案,第一個方案是 URL 攔截,他的優點無論是哪種 WebView 都是支持這種方式的,但是它的 URL 攔截延時高一點。第二個方案,主動輪詢,可以并發處理多條消息,但是如果在客戶端性能開銷大,第三個方案是我們現在正在用的,直接調用,但是他只支持 iOS 7 以上的系統。
接下來講一個非常重要的一個點,叫 模塊化拆解 ,其實像我們業務,每個業務,都需要在上面定制自己的橋協議,實際上這個也方便管理。 我們除了底下紅色,剛才講的消息通訊層以外,上面有模塊化的管理方式,像客戶端這邊,右側是客戶端這邊有模塊的管理模范,各個業務可以自己把自己的模塊注冊在這個里面,對應的JS層也有底層的封裝,左邊每一個JS對應右邊每一個模塊,會做一些模塊化拆解的工作。
開發調試
再說說我們怎么前端和客戶端怎么去開發,調試方式,其實現在方式是說,如果我們需要新增一個橋協議的話,前端會先準備一個 Demo 頁面,把這次需要新加的橋里面放在這個 Demo 頁面里面,客戶端基于這個 Demo 開發,會給前端打一個模擬器,前端會用這個模擬器安裝包,自己完成剩余的鏈條開發工作,這樣的好處是前端和客戶端可以同時開發。
小結
簡單回顧一下橋協議,橋協議通信用最簡單最直接的方式進行調用,橋協議的實現,最關鍵一點支持可擴展的能力,開發調試我們希望前端和客戶段可獨立并行開發。
3. H5 頁面如何接近 Native 的體驗?
第三個問題是指我們的 H5 頁面怎么去接近 Native 的體驗,在體驗差距上主要兩個方面。
頁面渲染瓶頸
第一個是前端的頁面代碼渲染,受限于 JS 的解析效率,以及手機硬件設備的一些性能。其實這個問題從應用開發的角度來說,是難以解決的。
資源加載緩慢
第二個方面是 H5 頁面是從服務器上下發的,客戶端的頁面在內存里面,頁面加載時間上面, H5 頁面和 Native 相比是有些差距的,但是這個差距我們可以通過一些方式彌補的,比如說我們做了一些 資源預加載 的方案。
在 資源預加載 方面,其實也很多方式,我主要列舉了一些,基本上每種方式我們都嘗試的做了。
第一種方式是說使用 WebView 自身的緩存機制。
如果我們在 APP 里面訪問一個頁面,短時間內再次訪問這個頁面的時候,會感覺到第二次打開的時候流暢很多,加載速度比第一次的時間要短。
這個就是因為,蘋果自己內部 Web 自身會做一些緩存,只要打開過的資源,他都會試著緩存在本地,第二次需要訪問的時候他直接從本地讀取,但是這個讀取其實是不太穩定的東西,關掉之后,或者說這種緩存之后,系統會自動把它清掉,我們沒法進行控制。
基于這個 WebView 自身的緩存,有一種資源預加載方案,我們在應用啟動的時候可以開一個像素的 WebView ,事先去訪問一下我們常用的資源,后續打開頁面的時候如果再用到這些資源他就可以從本地獲取到,頁面加載的時間會短一些。
第二種方案是說,我們自己去構建,自己管理緩存。
把這些需要預加載的資源放在 APP 里面,他可能是預制放進去的,也可能是后續下載的。
問題在于前端這些頁面怎么去緩存?
兩個方案,一個是,前端可以在 H5 打包的時候把里面的資源 URL 進行替換,這樣可以直接訪問本地的地址。客戶端可以攔截到這些網頁發出的所有請求做替換。
這個是我們做的資源預加載的方案,采用的剛才說的第二種方案,每當這個 WebView 發起資源請求的時候,我們會攔截到這些資源的請求,去本地檢查一下我們的這些靜態資源本地離線包有沒有。針對本地的緩存文件我們有些策略能夠及時的去更新它。為了安全考慮的話我們也做了一些預下載和安全包的一些加密的工作。
預加載方案的優勢?
-
第一,我們攔截了 WebView 里面發出的所有的請求,但是并沒有替換里面的前端應用的任何代碼,前端這套頁面代碼可以在 APP 內,或者其他的 APP 里面都可以直接訪問,他不需要為我們 APP 做定制化的東西。
-
第二,這些 URL 請求,他會直接帶上先前用戶操作所留下的 cookie 而都能夠留下來,因為我們沒有更改資源的 URL 地址。
-
第三,整個前端在用離線包的時候,緩存文件的時候是完全無感知的,前端只用管寫一個自己的頁面,客戶端會幫他處理好這樣一些靜態資源預加載的問題,有這個離線包的話,他加載速度會變快很多,沒有這些離線包加載速度會慢一些。如果版本不能跟他匹配的話,他的頁面也不會發生什么問題。
這個是我們當時做完之后,做完資源預加載之后的一些效果。比如說,這個圖里面可以看三個部分,一個是前端部分是沒有用資源預加載的下面,深色的部分是有資源預加載的效果,可以看到,如果把有些資源打成離線包放在本地的話,其實他的加載時間是可以縮短很多的。
另外一點可以橫向的看,其實像一二三,或者是這邊的一二三,三個頁面,其實本質上這三個頁面是一個購買流程人員,需要訪問到的路徑。
舉個例子,要進入第三個頁面,他一定會先打開第二個頁面,如果他打開第二個頁面,他一定會先打開第一個頁面。
前置篩選頁->車次列表頁->車次詳情頁
所以可以看到,整體的加載時間是不斷的縮短的。這個也就符合我們現在說的 Webview 自身是有一套緩存的。因為訪問后面頁面的時候有些資源其實在前面的頁面已經訪問過了,所以整個加載時間是不斷遞減的。
總結一下今天 Hybrid 化講的一些東西,包括我們做的動態路由切換,包括我們做的自定義橋協議,還有資源預加載的一些方案。
Hybrid vs Native
我們其實現在整個頁面里面既有 Hybrid 頁面也有 Native 頁面,那么我們是怎么做區分的?
一般來說Hybrid的項目一般是用在一些快速迭代試錯的地方。另外包括有一些非主流產品的頁面,我們傾向于用 Hybrid 的形式做.
但是像前端購買一些交易環節,特別核心的流程的話,我們一般情況下會用 Native 的形式去寫這些頁面,去提升,達到一個極致的用戶體驗。
三、其他方案對比
最后想對比一下,簡單聊一下我們現有的一些其他方案,當然這些方案,各個其他公司也正在去做。
1. React
第一個是 React 這邊,現在做了一些嘗試,因為 React 和安卓的平臺差異性是比較小,如果安卓端寫好代碼的話,成本很低,在項目發展初期的話,很好的去應用了這樣一種方式,減少成本。但是我們后面發現,當中也遇到了一些問題,如果其他同學有解決方案的話也歡迎分享一下。
第一穩定性沒有達到一個很好的標準,當然也有可能是我們在使用上還存在一些沒有掌握的地方。
第二個問題是人力的問題,我覺得可能比技術問題更復雜一點,就是說,其實現有市面上,我們很難在很短的時間內招到 10 個 iOS 的同學去做我們相應的開發。另外我們即使招到一些人,但是現有的公司里面培養體系,不太適合培養他們往更高層面發展。這個例子在后臺比較常見,像我們現在美團點評是后臺絕大部分都是用 Java 去寫的,說白一點,就是說 Java 這個東西,還是比較好招人,好大規模的去擴展去做事的。
2. Weex
Weex 方面,我們內部有一些調研和學習,但是人力的問題還是很凸顯。
3. 動態模板化
我們從業務發展的角度來說,也想獲得一些動態性的一些東西。希望考慮說把有一些局部的模塊能夠通過后臺下發的方式去做。我們的名字叫動態模板化,但是目前還是在做的階段,如果其他同學有相同的想法的話可以共同做一些分享。
互動問答
Q1:我有一個問題,剛才你說, JS 調用 Native 里面,有一個類似輪詢。
吳卓:我那句話意思是說,一次只能攔截到一條消息,如果用輪詢的方式的話,可以多條。因為最近應該很少有,最近幾期很少有美團的同學來這兒講課,如果大家對美團的其他的技術也興趣的話也可以提出來,我如果知道的話盡量也跟大家解釋一下。
Q2:這里哪一個頁面是 Hybrid 的?
吳卓:您下的是最近的版本嗎?舉個例子機票里面選一個國際的城市,你能看到的就是, Hybrid 的頁面。國際城市里面切換選擇日期的時候,看到的就是 Hybrid 的頁面。國際機票的列表也是用 Hybrid 走的。火車票里面以前是用 Hybrid 做的,現在的話,主流改成 Native 做的,當然如果出現一些緊急的情況,我們通過剛才的切換系統切換到原來的 Hybrid 上。
另外如果您打開交通里面的船票也是 Hybrid 的形式。因為我是做大交通業務的,所以說可能比較熟悉一點,向您推薦的也是我們的產品。從您點擊船票開始后面都是 Hybrid 的頁面,當然這個頁面里面有一些彈窗,有一些部分是Native做的。
Q3:你覺得 Hybrid 的模式和 Native 的模式,您覺得哪種可能是未來的發展趨勢,技術上。
吳卓:這是一個好問題。我只說一下我個人的觀點,不代表公司的觀點。首先我覺得從一個用戶體驗的角度來說,我更希望把所有頁面做成 Native 的,但是如果怎么說呢,我覺得比如像 WebView,我剛才說兩個問題,一個是說穩定性的問題,還有一個人力資源的問題,如果這兩個問題能解決的話,現在屬于觀望狀態,我們其實可以朝著這方面去做。因為我個人的觀點還是說,所有頁面都能盡可能的做成 Native 。在做 Hybrid 上,我們想盡方式讓它接近 Native 。
Q4:你們是如何管理 Hybrid 代碼更新的呢?
吳卓:離線包的形式肯定會增加內存的大小。我們的團隊做增量的更新,以減少這種資源包下載的流量,這是戰略空間的問題。
第二個是離線包里面有什么,最主要是一些靜態資源文件,包括JS,CSS。基本上H5頁面訪問,就是在訪問一個頁面的時候需要加載這些資源我們都可以從本地給他獲取。當然現在不是100%資源的離線化,一是考慮安全的因素,第二戰略方面的原因有些技術沒法做離線化。
來自:http://mp.weixin.qq.com/s/rNGD6SotKoO8frmxIU8-xw