1. 程式人生 > >聊一聊橋接(JSBridge)的原理

聊一聊橋接(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? 首先了解下`webView`,`webView`是移動端(原生)提供的執行JavaScript的環境,它是一種嵌入式瀏覽器,原生應用可以用它來展示網路內容。可與頁面JavaScript互動,實現混合開發,其中`Android`和`iOS`又有些不同: `Android`的`WebView`採用的是低版本和高版本使用了不同的webkit核心,4.4後直接使用了Chrome。 `iOS`中`UIWebView`算是自`IOS2`就有,但效能較差,特性支援較差,`WKWebView`是`iOS8`之後的升級版,效能更強特性支援也較好。 `WebView`控制元件除了能載入指定的url外,還可以對URL請求、JavaScript的對話方塊、載入進度、頁面互動進行強大的處理,之後會提到攔截請求、執行JS指令碼都依賴於此。 ##### 四、JSBridge的實現原理 >Web端和Native可以類比於Client/Server模式,Web端呼叫原生介面時就如同Client向Server端傳送一個請求類似,JSBridge在此充當類似於HTTP協議的角色,實現JSBridge主要是兩點: - 將Native端原生介面封裝成JavaScript介面 - 將Web端JavaScript介面封裝成原生介面 ![](https://img2020.cnblogs.com/blog/1525717/202103/1525717-20210315212108455-634817099.jpg) ##### 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的一種請求格式,格式如下: ``` :///?#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`方法攔截 IOS`UIWebView`使用`shouldStartLoadWithRequest`, IOS`WKWebView`則使用`decidePolicyForNavigationAction` 這種方式從早期就存在,相容性很好,但是由於是基於URL的方式,長度受到限制而且不太直觀,資料格式有限制,而且建立請求有時間耗時。 - 4.2.2 向Webview中注入JS API 這個方法會通過`webView`提供的介面,App將Native的相關介面注入到`JS`的`Context(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端程式碼
// 引入SDK ``` ``` // 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 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、jsBri