Hybrid 是移動端熱更新最常用的手段,限于 App Store 上架稽核時間較長,美團大衆點評也采取了該方案,歡迎來自美團大衆點旅遊業務 iOS 負責人**吳卓**分享**《美團大衆點評 酒旅方面 Hybrid 化建設》**。
本文來自于騰訊Bugly公衆号(weixinBugly),未經作者同意,請勿轉載,原文位址:http://mp.weixin.qq.com/s/rNGD6SotKoO8frmxIU8-xw
本期 T 沙龍探讨了移動端熱更新相關的話題。由于沙龍時間的限制,本期我們選取了美團的 Hybrid 化建設、去哪兒的跨平台 ListView 性能優化、微網誌 Android 端熱更新踩過的坑話題。還期待熱更新、熱修複哪些話題?歡迎留言給我們。也歡迎報名參加 T 沙龍分享自己開發中的心得。
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%資源的離線化,一是考慮安全的因素,第二戰略方面的原因有些技術沒法做離線化。
更多精彩内容歡迎關注bugly的微信公衆賬号:
騰訊 Bugly是一款專為移動開發者打造的品質監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合并功能幫助開發同學把每天上報的數千條 Crash 根據根因合并分類,每日日報會列出影響使用者數最多的崩潰,精準定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在釋出後快速的了解應用的品質情況,适配最新的 iOS, Android 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!
