天天看點

jsbridge原理_Hybrid App技術解析 -- 原理篇

引言

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

大家可以到github上和我進行讨論哈!

第二篇實戰篇

現有混合方案

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

  1. 基于 WebView UI 的基礎方案,市面上大部分主流 App 都有采用,例如微信JS-SDK,通過 JSBridge 完成 H5 與 Native 的雙向通訊,進而賦予H5一定程度的原生能力。
  2. 基于 Native UI 的方案,例如 React-Native、Weex。在賦予 H5 原生API能力的基礎上,進一步通過 JSBridge 将js解析成的虛拟節點樹(Virtual DOM)傳遞到 Native 并使用原生渲染。
  3. 另外還有近期比較流行的 小程式方案 ,也是通過更加定制化的 JSBridge,并使用雙 WebView 雙線程的模式隔離了JS邏輯與UI渲染,形成了特殊的開發模式,加強了 H5 與 Native 混合程度,提高了頁面性能及開發體驗。

以上的三種方案,其實同樣都是基于 JSBridge 完成的通訊層,第二三種方案,其實可以看做是在方案一的基礎上,繼續通過不同的新技術進一步提高了應用的混合程度。是以,JSBridge 也是整個混合應用最關鍵的部分,例如我們在設定微信分享時用到的 JS-SDK,wx對象 便是我們最常見的 JSBridge:

jsbridge原理_Hybrid App技術解析 -- 原理篇

方案選型

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

  • 需求 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 的機制。

jsbridge原理_Hybrid App技術解析 -- 原理篇

(一) 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

規則,通常我們的請求會帶有對應的協定開頭,例如常見的 https://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.addEventListener

window.dispatchEvent

這兩個基礎API;

  • 1.發送協定時,通過協定的唯一辨別注冊自定義事件,并将回調綁定到對應的事件上。
  • 2.用戶端完成對應的功能後,調用 Bridge 的dispatch API,直接攜帶 data 觸發該協定的自定義事件。
jsbridge原理_Hybrid App技術解析 -- 原理篇

通過事件的機制,會讓開發更符合我們前端的習慣,例如當你需要監聽用戶端的通知時,同樣隻需要在通過

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原理_Hybrid App技術解析 -- 原理篇

(三) 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。感謝!

繼續閱讀