1. 程式人生 > >【騰訊Bugly乾貨分享】美團大眾點評 Hybrid 化建設

【騰訊Bugly乾貨分享】美團大眾點評 Hybrid 化建設

本期 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 做的。

如果使用這種方式做的話,我們會遇到以下三個問題:

  1. H5 和 Native 上線時間不一致,如何銜接?
  2. H5 和 Native 之間如何進行通訊?
  3. 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 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!