聊一聊橋接(JSBridge)的原理

一、前言

如今的互聯網時代也稱移動互聯網時代,基本上每個人每天都會花費大量時間在移動設備上,早期的移動端應用大都使用原生開發(android,ios),而現在的移動開發技術選型上基本都是混合開發(Hybrid),混合開發是一種開發模式,指使用多種開發模型開發App,通常會涉及到兩大類技術:原生Native、Web H5

原生技術主要指iOS(Objective C)、Android(Java),原生開發效率較低,開發完成需要重新打包整個App,發佈依賴用戶的更新,性能較高功能覆蓋率更高,發佈流程較爲複雜
Web H5主要由HTML、CSS、JavaScript組成,Web可以更好的實現發佈更新,跨平臺也更加優秀,但性能較低,功能實現也受限

混合開發的意義就在於吸取兩者的優點,而且隨着手機硬件的升級迭代、系統(Android 5.0+、ISO 9.0+)對於Web特性的較好支持,H5的劣勢被逐漸縮小

原生、Web相互通信都離不開JSBridge

二、JSBridge做了些什麼?

Hybrid模式下,H5會經常需要使用Native的功能,比如打開二維碼掃描、調用原生頁面、獲取用戶信息等,同時Native也需要向Web端發送推送、更新狀態等,而JavaScript是運行在單獨的JS Context(Webview容器、JSCore等),與原生有運行環境的隔離,所以需要有一種機制實現Native端和Web端的雙向通信,這就是JSBridge以JavaScript引擎或Webview容器作爲媒介,通過協定協議進行通信,實現Native端和Web端雙向通信的一種機制。

通過JSBridge,Web端可以調用Native端的Java接口,同樣Native端也可以通過JSBridge調用Web端的JavaScript接口,實現彼此的相互調用。

三、什麼是WebView?

首先了解下webViewwebView是移動端(原生)提供的運行JavaScript的環境,它是一種嵌入式瀏覽器,原生應用可以用它來展示網絡內容。可與頁面JavaScript交互,實現混合開發,其中AndroidiOS又有些不同:

AndroidWebView採用的是低版本和高版本使用了不同的webkit內核,4.4後直接使用了Chrome。

iOSUIWebView算是自IOS2就有,但性能較差,特性支持較差,WKWebViewiOS8之後的升級版,性能更強特性支持也較好。

WebView控件除了能加載指定的url外,還可以對URL請求、JavaScript的對話框、加載進度、頁面交互進行強大的處理,之後會提到攔截請求、執行JS腳本都依賴於此。

四、JSBridge的實現原理

Web端和Native可以類比於Client/Server模式,Web端調用原生接口時就如同Client向Server端發送一個請求類似,JSBridge在此充當類似於HTTP協議的角色,實現JSBridge主要是兩點:

  • 將Native端原生接口封裝成JavaScript接口
  • 將Web端JavaScript接口封裝成原生接口
4.1 Native->Web

首先來說Native端調用Web端,這個比較簡單,JavaScript作爲解釋性語言,最大的一個特性就是可以隨時隨地地通過解釋器執行一段JS代碼,所以可以將拼接的JavaScript代碼字符串,傳入JS解析器執行就可以,JS解析器在這裏就是webView。

4.2 Web->Native

Web調用Native端主要有兩種方式

  • 4.2.1 攔截Webview請求的URL Schema
    URL Schema是類URL的一種請求格式,格式如下:
<protocol>://<host>/<path>?<qeury>#fragment

我們可以自定義JSBridge通信的URL Schema,比如:jsbridge://showToast?text=hello
Native加載WebView之後,Web發送的所有請求都會經過WebView組件,所以Native可以重寫WebView裏的方法,攔截Web發起的請求,我們對請求的格式進行判斷:
如果符合我們自定義的URL Schema,對URL進行解析,拿到相關操作、操作,進而調用原生Native的方法
如果不符合我們自定義的URL Schema,我們直接轉發,請求真正的服務

Web發送URL請求的方法有這麼幾種:

a標籤
location.href
使用iframe.src
發送ajax請求
這些方法,a標籤需要用戶操作,location.href可能會引起頁面的跳轉丟失調用,發送ajax請求Android沒有相應的攔截方法,所以使用iframe.src是經常會使用的方案

安卓提供了shouldOverrideUrlLoading方法攔截
IOSUIWebView使用shouldStartLoadWithRequest
IOSWKWebView則使用decidePolicyForNavigationAction
這種方式從早期就存在,兼容性很好,但是由於是基於URL的方式,長度受到限制而且不太直觀,數據格式有限制,而且建立請求有時間耗時。

  • 4.2.2 向Webview中注入JS API
    這個方法會通過webView提供的接口,App將Native的相關接口注入到JSContext(window)的對象中,一般來說這個對象內的方法名與Native相關方法名是相同的,Web端就可以直接在全局window下使用這個全局JS對象,進而調用原生端的方法。

Android(4.2+)提供了addJavascriptInterface注入:

// 注入全局JS對象
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");

class NativeBridge {
  private Context ctx;
  NativeBridge(Context ctx) {
    this.ctx = ctx;
  }

  // 增加JS調用接口
  @JavascriptInterface
  public void showNativeDialog(String text) {
    new AlertDialog.Builder(ctx).setMessage(text).create().show();
  }
}

在Web端直接調用這個方法即可:

window.NativeBridge.showNativeDialog('hello');
4.3 帶回調的調用

上面已經說到了Native、Web間雙向通信的兩種方法,但站在一端而言還是一個單向通信的過程 ,比如站在Web的角度:Web調用Native的方法,Native直接相關操作但無法將結果返回給Web,但實際使用中會經常需要將操作的結果返回,也就是JS回調。

所以在對端操作並返回結果,有輸入有輸出纔是完整的調用,那如何實現呢?

其實基於之前的單向通信就可以實現,我們在一端調用的時候在參數中加一個callbackId標記對應的回調,對端接收到調用請求後,進行實際操作,如果帶有callbackId,對端再進行一次調用,將結果、callbackId回傳回來,這端根據callbackId匹配相應的回調,將結果傳入執行就可以了。

五、開源的JSBridge

實現一個完整的JSBridge是比較複雜的,需要考慮一些低端機型的兼容問題、同步異步調用問題,好在已經有開源的JSBridge供我們直接使用了:

DSBridge,主要通過注入API的形式,DSBridge for Android、DSBridge for IOS
JsBridge,主要通過攔截URL Schema,JsBridge
以DSBridge-Android爲例:

// Web端代碼
<body>
  <div>
    <button id="showBtn">獲取Native輸入,以Web彈窗展現</button>
  </div>
</body>
// 引入SDK
<script src="https://unpkg.com/[email protected]/dist/dsbridge.js"></script>
<script>
  const showBtn = document.querySelector('#showBtn');
  showBtn.addEventListener('click', e => {
    // 注意,這裏代碼不同:SDK在全局註冊了dsBridge,通過call調用Native方法
    dsBridge.call('getNativeEditTextValue', '', value => {
      window.alert('Native輸入值' + value);
    })
  });
</script>
// Android代碼
// 使用dwebView替換原生webView
dwebView.addJavascriptObject(new JsApi(), null);

class JSApi {
  private Context ctx;
  public JSApi (Context ctx) {
    this.ctx = ctx;
  }

  @JavascriptInterface
  public void getNativeEditTextValue(Object msg, CompletionHandler<String> handler) {
    String value = ((MainActivity)ctx).editText.getText().toString();
    // 通過handler將value傳給Web端,實現回調的JSB調用
    handler.completed(value);
  }
}

Hybrid開發是目前移動端開發的主流技術選項,其中Native和Web端的雙向通信就離不開JSBridge
其中Native調用Web端是直接在JS的Context直接執行JS代碼,Web端調用Native端有兩種方法,一種是基於URL Schema的攔截操作,另一種是向JS的Context(window)注入Api,其中注入Api是目前最好的選擇。完整的調用是雙向通信,需要一個回調函數,技術實現上就是使用了兩次單向通信

開源的JSBridge:DSBridge、jsBridge。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章