隨着 Web 技術和移動設備的快速發展,Hybrid 技術已經成爲一種最主流最多見的方案。一套好的 Hybrid架構方案 能讓 App 既能擁有極致的體驗和性能,同時也能擁有 Web技術 靈活的開發模式、跨平臺能力以及熱更新機制。javascript
Hybrid App,俗稱混合應用,即混合了 Native技術 與 Web技術 進行開發的移動應用。如今比較流行的混合方案主要有三種,主要是在UI渲染機制上的不一樣:前端
以上的三種方案,其實一樣都是基於 JSBridge 完成的通信層,第二三種方案,其實能夠看作是在方案一的基礎上,繼續經過不一樣的新技術進一步提升了應用的混合程度。所以,JSBridge 也是整個混合應用最關鍵的部分。java
Hybrid App的本質,實際上是在原生的 App 中,使用 WebView 做爲容器直接承載 Web頁面。所以,最核心的點就是 Native端 與 H5端 之間的雙向通信層,其實這裏也能夠理解爲咱們須要一套跨語言通信方案,來完成 Native(Java/Objective-c/...) 與 JavaScript 的通信。這個方案就是咱們所說的 JSBridge,而實現的關鍵即是做爲容器的 WebView,一切的原理都是基於 WebView 的機制。web
基於 WebView 的機制和開放的 API, 實現這個功能有三種常見的方案:小程序
第二三種機制的原理是相似的,都是經過對 WebView 信息冒泡傳遞的攔截,從而達到通信的,接下來咱們主要從 原理-定製協議-攔截協議-參數傳遞-回調機制 5個方面詳細闡述下第三種方案 -- URL攔截方案。安全
在 WebView 中發出的網絡請求,客戶端都能進行監聽和捕獲服務器
咱們須要制定一套URL Scheme規則,一般咱們的請求會帶有對應的協議開頭,例如常見的 https://xxx.com 或者 file://1.jpg,表明着不一樣的含義。咱們這裏能夠將協議類型的請求定製爲:微信
xxcommand://xxxx?param1=1¶m2=2
這裏有幾個須要注意點的是:網絡
(1) xxcommand:// 只是一種規則,能夠根據業務進行制定,使其具備含義架構
不一樣的協議頭表明着不一樣的含義,這樣便能清楚知道每一個協議的適用範圍。
(2) 這裏不要使用 location.href 發送,由於其自身機制有個問題是同時併發屢次請求會被合併成爲一次,致使協議被忽略,而併發協議實際上是很是常見的功能。
(3) 一般考慮到安全性,須要在客戶端中設置域名白名單或者限制。
客戶端能夠經過 API 對 WebView 發出的請求進行攔截:
當解析到請求 URL 頭爲制定的協議時,便不發起對應的資源請求,而是解析參數,並進行相關功能或者方法的調用,完成協議功能的映射。
因爲協議的本質實際上是發送請求,這屬於一個異步的過程,所以咱們便須要處理對應的回調機制。這裏咱們採用的方式是JS的事件系統,這裏咱們會用到 window.addEventListener
和 window.dispatchEvent
這兩個基礎API;
因爲 WebView 對 URL 會有長度的限制,所以常規的經過 search參數 進行傳遞的方式便具備一個問題,既 當須要傳遞的參數過長時,可能會致使被截斷,例如傳遞base64或者傳遞大量數據時。
所以咱們須要制定新的參數傳遞規則,咱們使用的是函數調用的方式。這裏的原理主要是基於:
Native 能夠直接調用 JS 方法並直接獲取函數的返回值。
咱們只須要對每條協議標記一個惟一標識,並把參數存入參數池中,到時客戶端再經過該惟一標識從參數池中獲取對應的參數便可。
因爲 Native 能夠算做 H5 的宿主,所以擁有更大的權限,上面也提到了 Native 能夠經過 WebView API直接執行 Js 代碼。這樣的權限也就讓這個方向的通信變得十分的便捷。
// Swift webview.stringByEvaluatingJavaScriptFromString("alert('NativeCall')")
// 調用js中的JSBridge.trigger方法 // 該方法的弊端是沒法獲取函數返回值; webView.loadUrl("javascript:JSBridge.trigger('NativeCall')")
基於上面的原理,咱們已經明白 JSBridge 最基礎的原理,而且能實現 Native <=> H5 的雙向通信機制了。
接下來,咱們來理下代碼上須要的資源。實現這套方案,從上圖能夠看出,其實能夠分爲兩個部分:
咱們這裏的作法是,將這兩部分一塊兒封裝成一個 Native SDK,由客戶端統一引入。客戶端在初始化一個 WebView 打開頁面時,若是頁面地址在白名單中,會直接在 HTML 的頭部注入對應的 bridge.js。這樣的作法有如下的好處:
這裏有一點須要注意的是,協議的調用,必定是須要確保執行在bridge.js 成功注入後。因爲客戶端的注入行爲屬於一個附加的異步行爲,從H5方很難去捕捉準確的完成時機,所以這裏須要經過客戶端監聽頁面完成後,基於上面的事件回調機制通知 H5端,頁面中便可經過window.addEventListener('bridgeReady', e => {})
進行初始化。
將 H5 接入 App 中一般有兩種方式:
(1) 在線H5,這是最多見的一種方式。咱們只須要將H5代碼部署到服務器上,只要把對應的 URL地址 給到客戶端,用 WebView 打開該URL,便可嵌入。該方式的好處在於:
但相對的,這種方式也有對應的缺點:
一般,這種方式更適用在一些比較輕量級的頁面上,例如一些幫助頁、提示頁、使用攻略等頁面。這些頁面的特色是功能性不強,不太須要複雜的功能協議,且不須要離線使用。在一些第三方頁面接入上,也會使用這種方式,例如咱們的頁面調用微信JS-SDK。
(2) 內置包H5,這是一種本地化的嵌入方式,咱們須要將代碼進行打包後下發到客戶端,並由客戶端直接解壓到本地儲存中。一般咱們運用在一些比較大和比較重要的模塊上。其優勢是:
但同時,它的劣勢也十分明顯:
這兩種接入方式均有本身的優缺點,應該根據不一樣場景進行選擇。