Hybrid App技術解析 -- 原理篇

引言

隨着 Web 技術和移動設備的快速發展,Hybrid 技術已經成爲一種最主流最多見的方案。一套好的 Hybrid架構方案 能讓 App 既能擁有極致的體驗和性能,同時也能擁有 Web技術 靈活的開發模式、跨平臺能力以及熱更新機制,想一想是否是都雞凍不已。。😄。本系列文章是公司在這方面實踐的一個總結,包含了原理解析、方案選型與實現、實踐優化等方面。javascript

你們能夠到github上和我進行討論哈!前端

第二篇實戰篇java

現有混合方案

Hybrid App,俗稱混合應用,即混合了 Native技術 與 Web技術 進行開發的移動應用。如今比較流行的混合方案主要有三種,主要是在UI渲染機制上的不一樣:git

  1. 基於 WebView UI 的基礎方案,市面上大部分主流 App 都有采用,例如微信JS-SDK,經過 JSBridge 完成 H5 與 Native 的雙向通信,從而賦予H5必定程度的原生能力。github

  2. 基於 Native UI 的方案,例如 React-Native、Weex。在賦予 H5 原生API能力的基礎上,進一步經過 JSBridge 將js解析成的虛擬節點樹(Virtual DOM)傳遞到 Native 並使用原生渲染。web

  3. 另外還有近期比較流行的小程序方案,也是經過更加定製化的 JSBridge,並使用雙 WebView 雙線程的模式隔離了JS邏輯與UI渲染,造成了特殊的開發模式,增強了 H5 與 Native 混合程度,提升了頁面性能及開發體驗。小程序

以上的三種方案,其實一樣都是基於 JSBridge 完成的通信層,第二三種方案,其實能夠看作是在方案一的基礎上,繼續經過不一樣的新技術進一步提升了應用的混合程度。所以,JSBridge 也是整個混合應用最關鍵的部分,例如咱們在設置微信分享時用到的 JS-SDK,wx對象 即是咱們最多見的 JSBridge:安全

方案選型

任何技術方案的選型,其實都應該基於使用場景和現有條件。基於公司現有狀況的幾點考慮,在方案一上進一步優化,更加適合咱們的需求。服務器

  • 需求 Web技術 快速迭代、靈活開發的特色和線上熱更新的機制。微信

  • 產品的核心能力是強大的拍照與底層圖片處理能力,所以單純的 H5技術能作的事很是有限,不能知足需求,經過 Hybrid 技術來強化H5,即是一種必需。

  • 公司業務上,並無很是複雜的UI渲染需求,並且 App 中的一系列原生 UI組件 已經很是成熟,所以咱們並不強需相似 RN 這樣的方案。

所以,如何既能利用 H5 強大的開發和迭代能力,又能賦予 H5 強大的底層能力和用戶體驗,同時能複用現有的成熟 Native組件,便成爲了咱們最大的需求點 -- 一套完整又強大的 Hybrid技術架構方案。😠

Hybrid技術原理

Hybrid App的本質,實際上是在原生的 App 中,使用 WebView 做爲容器直接承載 Web頁面。所以,最核心的點就是 Native端 與 H5端 之間的雙向通信層,其實這裏也能夠理解爲咱們須要一套跨語言通信方案,來完成 Native(Java/Objective-c/...) 與 JavaScript 的通信。這個方案就是咱們所說的 JSBridge,而實現的關鍵即是做爲容器的 WebView,一切的原理都是基於 WebView 的機制。

(一) JavaScript 通知 Native

基於 WebView 的機制和開放的 API, 實現這個功能有三種常見的方案:

  • API注入,原理其實就是 Native 獲取 JavaScript環境上下文,並直接在上面掛載對象或者方法,使 js 能夠直接調用,Android 與 IOS 分別擁有對應的掛載方式。

  • WebView 中的 prompt/console/alert 攔截,一般使用 prompt,由於這個方法在前端中使用頻率低,比較不會出現衝突;

  • WebView URL Scheme 跳轉攔截

第二三種機制的原理是相似的,都是經過對 WebView 信息冒泡傳遞的攔截,從而達到通信的,接下來咱們主要從 原理-定製協議-攔截協議-參數傳遞-回調機制 5個方面詳細闡述下第三種方案 -- URL攔截方案。

1. 實現原理

在 WebView 中發出的網絡請求,客戶端都能進行監聽和捕獲

2. 協議的定製

咱們須要制定一套URL Scheme規則,一般咱們的請求會帶有對應的協議開頭,例如常見的 xxx.com 或者 file://1.jpg,表明着不一樣的含義。咱們這裏能夠將協議類型的請求定製爲:

xxcommand://xxxx?param1=1&param2=2

這裏有幾個須要注意點的是:

(1) xxcommand:// 只是一種規則,能夠根據業務進行制定,使其具備含義,例如咱們定義 xxcommand:// 爲公司全部App系通用,爲通用工具協議:

xxcommand://getProxy?h=1

而定義 xxapp:// 爲每一個App單獨的業務協議。

xxapp://openCamera?h=2

不一樣的協議頭表明着不一樣的含義,這樣便能清楚知道每一個協議的適用範圍。

(2) 這裏不要使用 location.href 發送,由於其自身機制有個問題是同時併發屢次請求會被合併成爲一次,致使協議被忽略,而併發協議實際上是很是常見的功能。咱們會使用建立 iframe 發送請求的方式。

(3) 一般考慮到安全性,須要在客戶端中設置域名白名單或者限制,避免公司內部業務協議被第三方直接調用。

3.協議的攔截

客戶端能夠經過 API 對 WebView 發出的請求進行攔截:

  • IOS上: shouldStartLoadWithRequest

  • Android: shouldOverrideUrlLoading

當解析到請求 URL 頭爲制定的協議時,便不發起對應的資源請求,而是解析參數,並進行相關功能或者方法的調用,完成協議功能的映射。

4.協議回調

因爲協議的本質實際上是發送請求,這屬於一個異步的過程,所以咱們便須要處理對應的回調機制。這裏咱們採用的方式是JS的事件系統,這裏咱們會用到 window.addEventListenerwindow.dispatchEvent這兩個基礎API;

  • 1.發送協議時,經過協議的惟一標識註冊自定義事件,並將回調綁定到對應的事件上。

  • 2.客戶端完成對應的功能後,調用 Bridge 的dispatch API,直接攜帶 data 觸發該協議的自定義事件。

經過事件的機制,會讓開發更符合咱們前端的習慣,例如當你須要監聽客戶端的通知時,一樣只須要在經過 addEventListener 進行監聽便可。

Tips: 這裏有一點須要注意的是,應該避免事件的屢次重複綁定,所以當惟一標識重置時,須要removeEventListener對應的事件。

5.參數傳遞方式

因爲 WebView 對 URL 會有長度的限制,所以常規的經過 search參數 進行傳遞的方式便具備一個問題,既 當須要傳遞的參數過長時,可能會致使被截斷,例如傳遞base64或者傳遞大量數據時。

所以咱們須要制定新的參數傳遞規則,咱們使用的是函數調用的方式。這裏的原理主要是基於:

Native 能夠直接調用 JS 方法並直接獲取函數的返回值。

咱們只須要對每條協議標記一個惟一標識,並把參數存入參數池中,到時客戶端再經過該惟一標識從參數池中獲取對應的參數便可。

(二) Native 通知 Javascript

因爲 Native 能夠算做 H5 的宿主,所以擁有更大的權限,上面也提到了 Native 能夠經過 WebView API直接執行 Js 代碼。這樣的權限也就讓這個方向的通信變得十分的便捷。

  • IOS: stringByEvaluatingJavaScriptFromString
// Swift
webview.stringByEvaluatingJavaScriptFromString("alert('NativeCall')")
複製代碼
  • Android: loadUrl (4.4-)
// 調用js中的JSBridge.trigger方法
// 該方法的弊端是沒法獲取函數返回值;
webView.loadUrl("javascript:JSBridge.trigger('NativeCall')")
複製代碼

Tips: 當系統低於4.4時,evaluateJavascript 是沒法使用的,所以單純的使用 loadUrl 沒法獲取 JS 返回值,這時咱們須要使用前面提到的 prompt 的方法進行兼容,讓 H5端 經過 prompt 進行數據的發送,客戶端進行攔截並獲取數據。

  • Android: evaluateJavascript (4.4+)
// 4.4+後使用該方法即可調用並獲取函數返回值;
mWebView.evaluateJavascript("javascript:JSBridge.trigger('NativeCall')", 	 new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        //此處爲 js 返回的結果
    }
});
複製代碼

基於上面的原理,咱們已經明白 JSBridge 最基礎的原理,而且能實現 Native <=> H5 的雙向通信機制了。

(三) JSBridge 的接入

接下來,咱們來理下代碼上須要的資源。實現這套方案,從上圖能夠看出,其實能夠分爲兩個部分:

  • JS部分(bridge): 在JS環境中注入 bridge 的實現代碼,包含了協議的拼裝/發送/參數池/回調池等一些基礎功能。

  • Native部分(SDK): 在客戶端中 bridge 的功能映射代碼,實現了URL攔截與解析/環境信息的注入/通用功能映射等功能。

咱們這裏的作法是,將這兩部分一塊兒封裝成一個 Native SDK,由客戶端統一引入。客戶端在初始化一個 WebView 打開頁面時,若是頁面地址在白名單中,會直接在 HTML 的頭部注入對應的 bridge.js。這樣的作法有如下的好處:

  • 雙方的代碼統一維護,避免出現版本分裂的狀況。有更新時,只要由客戶端更新SDK便可,不會出現版本兼容的問題;

  • App的接入十分方便,只須要按文檔接入最新版本的SDK,便可直接運行整套Hybrid方案,便於在多個App中快速的落地;

  • H5端無需關注,這樣有利於將 bridge 開放給第三方頁面使用。

這裏有一點須要注意的是,協議的調用,必定是須要確保執行在bridge.js 成功注入後。因爲客戶端的注入行爲屬於一個附加的異步行爲,從H5方很難去捕捉準確的完成時機,所以這裏須要經過客戶端監聽頁面完成後,基於上面的事件回調機制通知 H5端,頁面中便可經過window.addEventListener('bridgeReady', e => {})進行初始化。

(四) App中 H5 的接入方式

將 H5 接入 App 中一般有兩種方式:

(1) 在線H5,這是最多見的一種方式。咱們只須要將H5代碼部署到服務器上,只要把對應的 URL地址 給到客戶端,用 WebView 打開該URL,便可嵌入。該方式的好處在於:

  • 獨立性強,有很是獨立的開發/調試/更新/上線能力;
  • 資源放在服務器上,徹底不會影響客戶端的包體積;
  • 接入成本很低,徹底的熱更新機制。

但相對的,這種方式也有對應的缺點:

  • 徹底的網絡依賴,在離線的狀況下沒法打開頁面;
  • 首屏加載速度依賴於網絡,網絡較慢時,首屏加載也較慢;

一般,這種方式更適用在一些比較輕量級的頁面上,例如一些幫助頁、提示頁、使用攻略等頁面。這些頁面的特色是功能性不強,不太須要複雜的功能協議,且不須要離線使用。在一些第三方頁面接入上,也會使用這種方式,例如咱們的頁面調用微信JS-SDK。

(2) 內置包H5,這是一種本地化的嵌入方式,咱們須要將代碼進行打包後下發到客戶端,並由客戶端直接解壓到本地儲存中。一般咱們運用在一些比較大和比較重要的模塊上。其優勢是:

  • 因爲其本地化,首屏加載速度快,用戶體驗更爲接近原生;
  • 能夠不依賴網絡,離線運行;

但同時,它的劣勢也十分明顯:

  • 開發流程/更新機制複雜化,須要客戶端,甚至服務端的共同協做;
  • 會相應的增長 App 包體積;

這兩種接入方式均有本身的優缺點,應該根據不一樣場景進行選擇。

總結

本文主要解析瞭如今Hybrid App的發展示狀和其基礎原理,包含了

  • JavaScript 通知 Native
  • Native 通知 Javascript
  • JSBridge 的接入
  • H5 的接入

只有在瞭解了其最本質的實現原理後,才能對這套方案進行實現以及進一步的優化。接下來,咱們將基於上面的理論,繼續探討如何把這套方案的真正代碼實現以及方案優化方案,請繼續第二篇實戰篇。歡迎你們一塊兒討論!更多文章內容請關注下面公衆號或到github。感謝!😊

相關文章
相關標籤/搜索