一篇文章了解 JsBridge
一、jsbridge 定義
JSBridge 是一種 JS 實現的 Bridge,連接著橋兩端的 Native 和 H5。它在 APP 內方便地讓 Native 調用 JS,JS 調用 Native ,是雙向通信的通道。JSBridge 主要提供了 JS 調用 Native 代碼的能力,實現原生功能如查看本地相冊、打開攝像頭、指紋支付等。
二、JSBridge 用途
JSBridge 就像其名稱中的『Bridge』的意義一樣,是 Native 和非 Native 之間的橋梁,它的核心是 構建 Native 和非 Native 間消息通信的通道,而且是 雙向通信的通道。
雙向通信的通道:
1) JS 向 Native 發(fā)送消息: 調用相關功能、通知 Native 當前 JS 的相關狀態(tài)等。
2) Native 向 JS 發(fā)送消息: 回溯調用結果、消息推送、通知 JS 當前 Native 的狀態(tài)等。
H5與Native交互如下圖:
三、JSBridge 的實現原理
JavaScript 是運行在一個單獨的 JS Context 中(例如,WebView 的 Webkit 引擎、JSCore)。由于這些 Context 與原生運行環(huán)境的天然隔離,我們可以將這種情況與 RPC(Remote Procedure Call,遠程過程調用)通信進行類比,將 Native 與 JavaScript 的每次互相調用看做一次 RPC 調用。
在 JSBridge 的設計中,可以把前端看做 RPC 的客戶端,把 Native 端看做 RPC 的服務器端,從而 JSBridge 要實現的主要邏輯就出現了:通信調用(Native 與 JS 通信) 和句柄解析調用。
四、JSBridge 的通信原理
一、JavaScript 調用 Native的方式
JavaScript 調用 Native 的方式,主要有兩種:注入 API 和 攔截 URL SCHEME。
1、注入API
注入 API 方式的主要原理是,通過 WebView 提供的接口,向JavaScript 的 Context(window)中注入對象或者方法,讓 JavaScript 調用時,直接執(zhí)行相應的 Native 代碼邏輯,達到 JavaScript 調用 Native 的目的。
對于 iOS 的 UIWebView,實例如下:
- JSContext *context = [uiWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
- context[@"postBridgeMessage"] = ^(NSArray<NSArray *> *calls) {
- // Native 邏輯
- };
- 前端調用方式:
- window.postBridgeMessage(message);
2、攔截 URL SCHEME
先解釋一下 URL SCHEME:URL SCHEME是一種類似于url的鏈接,是為了方便app直接互相調用設計的,形式和普通的 url 近似,主要區(qū)別是 protocol 和 host 一般是自定義的,
例如: qunarhy://hy/url?url=ymfe.tech,protocol 是 qunarhy,host 則是 hy。
攔截 URL SCHEME 的主要流程是:Web 端通過某種方式(例如 iframe.src)發(fā)送 URL Scheme 請求,之后 Native 攔截到請求并根據 URL SCHEME(包括所帶的參數)進行相關操作。
在時間過程中,這種方式有一定的缺陷:
1) 使用 iframe.src 發(fā)送 URL SCHEME 會有 url 長度的隱患。
2) 創(chuàng)建請求,需要一定的耗時,比注入 API 的方式調用同樣的功能,耗時會較長。
因此:JavaScript 調用 Native 推薦使用注入 API 的方式
二、Native 調用 JavaScript 的方式
相比于 JavaScript 調用 Native, Native 調用 JavaScript 較為簡單,直接執(zhí)行拼接好的 JavaScript 代碼即可。
從外部調用 JavaScript 中的方法,因此 JavaScript 的方法必須在全局的 window 上。
對于 iOS 的 UIWebView,示例如下:
- result = [uiWebview stringByEvaluatingJavaScriptFromString:javaScriptString];
- * javaScriptString為JavaScript 代碼串
對于 iOS 的 WKWebView,示例如下:
- [wkWebView evaluateJavaScript:javaScriptString completionHandler:completionHandler];
三、通信原理總結
通信原理是 JSBridge 實現的核心
1)JavaScript 調用 Native 推薦使用 注入 API 的方式(iOS6 忽略,Android 4.2以下使用 WebViewClient 的 onJsPrompt 方式)。
2)Native 調用 JavaScript 則直接執(zhí)行拼接好的 JavaScript 代碼即可。
四、JSBridge 接口實現
JSBridge 的接口主要功能有兩個:調用 Native(給 Native 發(fā)消息) 和 被 Native 調用(接收 Native 消息)。因此,JSBridge 可以設計如下:
- window.JSBridge = {
- // 調用 Native
- invoke: function(msg) {
- // 判斷環(huán)境,獲取不同的 nativeBridge
- nativeBridge.postMessage(msg);
- },
- receiveMessage: function(msg) {
- // 處理 msg
- }
- };
在上面的文章中,提到過 RPC 中有一個非常重要的環(huán)節(jié)是 句柄解析調用 ,這點在 JSBridge 中體現為 句柄與功能對應關系。同時,我們將句柄抽象為 橋名(BridgeName),最終演化為 一個 BridgeName 對應一個 Native 功能或者一類 Native 消息?;诖它c,JSBridge 的實現可以優(yōu)化為如下:
- window.JSBridge = {
- // 調用 Native
- invoke: function(bridgeName, data) {
- // 判斷環(huán)境,獲取不同的 nativeBridge
- nativeBridge.postMessage({
- bridgeName: bridgeName,
- data: data || {}
- });
- },
- receiveMessage: function(msg) {
- var bridgeName = msg.bridgeName,
- data = msg.data || {};
- // 具體邏輯
- }
- };
對于 JSBridge 的 Callback ,其實就是 RPC 框架的回調機制。當然也可以用更簡單的 JSONP 機制解釋:
當發(fā)送 JSONP 請求時,url 參數里會有 callback 參數,其值是 當前頁面唯一 的,而同時以此參數值為 key 將回調函數存到 window 上,隨后,服務器返回 script 中,也會以此參數值作為句柄,調用相應的回調函數。
整體流程:
在 Native 端配合實現 JSBridge 的 JavaScript 調用 Native 邏輯也很簡單,主要的代碼邏輯是:接收到 JavaScript 消息 => 解析參數,拿到 bridgeName、data 和 callbackId => 根據 bridgeName 找到功能方法,以 data 為參數執(zhí)行 => 執(zhí)行返回值和 callbackId 一起回傳前端。
Native調用 JavaScript 也同樣簡單,直接自動生成一個唯一的 ResponseId,并存儲句柄,然后和 data 一起發(fā)送給前端即可。
五、JSBridge 如何引用
對于 JSBridge 的引用,常用有兩種方式,各有利弊。
1) 由 Native 端進行注入
注入方式和 Native 調用 JavaScript 類似,直接執(zhí)行橋的全部代碼。
優(yōu)點:橋的版本很容易與 Native 保持一致,Native 端不用對不同版本的 JSBridge 進行兼容;與此同時,
缺點:注入時機不確定,需要實現注入失敗后重試的機制,保證注入的成功率,同時 JavaScript 端在調用接口時,需要優(yōu)先判斷 JSBridge 是否已經注入成功。
2) 由 JavaScript 端引用
直接與 JavaScript 一起執(zhí)行。與由 Native 端注入正好相反。
優(yōu)點:JavaScript 端可以確定 JSBridge 的存在,直接調用即可;
缺點:如果橋的實現方式有更改,JSBridge 需要兼容多版本的 Native Bridge 或者 Native Bridge 兼容多版本的 JSBridge。