Hybrid開發——Native與H5的通信框架JSBridge實現(1)

特別說明


首先說明本文并非原創,原文在此:https://www.cnblogs.com/dailc/p/5931324.html#callback-format。相關代碼會放在https://github.com/Samuel2306/JSBridge


什么是JSBridge


小伙伴如果如果對于Hybrid并沒有什么概念,請先閱讀筆者的另外一篇文章《Hybrid開發——Android與H5的親密接觸》。JSBridge就是一個簡化Native與JS通信的框架。JSBridge定義了統一的通信過程:Native只通過一個固定的橋對象調用JS,JS也只通過固定的橋對象調用Native,具體流程如下圖所示:

JSBridge定義的通信過程

說明:JS觸發原生的回調函數的過程其實也是觸發一個url schema


url scheme介紹

? url scheme是一種類似于url的鏈接,是為了方便app直接互相調用設計的

可以用系統的OpenURI打開一個類似于url的鏈接(可拼入參數),然后系統會進行判斷,如果是系統的url scheme,則打開系統應用,否則找看是否有app注冊這種scheme,打開對應app需要注意的是,這種scheme必須原生app注冊后才會生效,如微信的scheme為(weixin://)

??本文JSBridge中的url scheme則是仿照上述的形式的一種方式

app不會注冊對應的scheme,而是由前端頁面通過某種方式觸發scheme(如用iframe.src),然后Native用某種方法捕獲對應的url觸發事件,然后拿到當前的觸發url,根據定義好的協議,分析當前觸發了那種方法。簡而言之就是url => 方法(對應關系是JS開發與Android開發商量定義好的)。


為什么要用JSBridge

在筆者的《Hybrid開發——Android與H5的親密接觸》一文中,我們知道Android已經可以跟JS進行交互,那為什么還要這種通過url scheme的JSBridge方式呢,原因大致如下:

? Android4.2以下,addJavascriptInterface方式有安全漏掉

??iOS7以下, JS無法調用Native

url scheme交互方式是一套現有的成熟方案,可以完美兼容各種版本,不存在上述問題。而且JSBridge將Native與JS的交互方式進行解耦,提供了一個可擴展的,高可用的,穩定的解決方案。


實現一個JSBridge


實現步驟大致如下:

第一步: 設計出一個Native與JS交互的全局橋對象

第二步: JS調用Native功能實現

第三步: Native監聽api調用

第四步: Native分析url-參數和回調的格式

第五步: Native調用JS功能實現

第六步: H5中api方法的注冊

第一步: 設計出一個Native與JS交互的全局橋對象

我們規定,JS和Native之間的通信必須通過一個H5全局對象JSBridge來實現,該全局對象有如下特點:

? 該對象名為“JSBridge”,是H5頁面中全局對象window的一個屬性

window.JSBridge = JSBridge

? 該對象有如下方法

1、registerHandler(?String handlerName, Function handler):供H5調用。用來注冊JS方法,Native可通過JSBridge調用注冊的JS方法。調用registerHandler方法后,被注冊的方法 handler 會被保存到本地變量 messageHandlers?中。

2、callHandler(?String handlerName, JSON data, Function callback):供H5調用。H5調用原生開放的api,調用后實際上還是本地通過url scheme觸發。調用時會將回調 callback 的 id(由callHandler函數生成)存放到本地變量responseCallbacks中。

3、_handleMessageFromNative(?JSON?):供Native調用。原生調用H5頁面注冊的方法,或者通知H5頁面執行回調方法(H5調用原生方法時,會將回調函數作為參數進行傳遞,就是為了讓原生能在適當的時候通過JSBridge調用回調函數,也就是responseCallbacks里面對應的函數)。

對象功能結構圖


第二步: JS調用Native功能實現

上一步我們已經定義好了JSBridge對象,H5可以通過callHandler方法來調用原生的API,那么callHandler內部經歷了一個怎么樣的過程呢?接下來我們就來說說callHandler(?String?handlerName, JSON data, Function callback)函數內部實現過程。

在執行callHandler(?String?handlerName, JSON data, Function callback)時,內部經歷了以下步驟:

(1)首先判斷H5調用該方法的時候有沒有傳入回調函數。如果有回調函數,則生成一個回調函數id, 并將id和對應的回調函數添加進入回調函數的集合?responseCallbacks 中

(2)通過特定的參數轉換方法,將傳入的數據(data), 方法名(handlerName)一起,拼接成一個url scheme

// url scheme的格式如下,基本有用信息就是后面的callbackId,handlerName與data?

// 原生捕獲到這個scheme后會進行分析

var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data

(3)使用內部早就創建好的一個隱藏iframe來觸發scheme

//創建隱藏iframe過程

var messagingIframe = document.createElement('iframe');

messagingIframe.style.display = 'none';

document.documentElement.appendChild(messagingIframe);

//觸發scheme

messagingIframe.src = uri;

注意1:正常來說是可以通過window.location.href達到發起網絡請求的效果的,但是有一個很嚴重的問題,就是如果我們連續多次修改window.location.href的值,在Native層只能接收到最后一次請求,前面的請求都會被忽略掉。所以JS端發起網絡請求的時候,需要使用iframe,這樣就可以避免這個問題。

注意2:H5調用Native時,Native處理完畢后一定要及時通知H5進行回調,要不然這個回調函數不會自動銷毀,多了后會引發內存泄漏

第三步: Native監聽api調用

在上一步中,我們已經成功在H5頁面中觸發scheme,那么Native如何捕獲scheme被觸發呢?接下來我們以Android為例,說說Android如何捕獲url scheme,不過在此之前我們先來簡單了解幾個東西:WebView、WebViewClient、WebChromeClient。

在Android的Webview設計中,不是所有功能都由WebView類來實現的,部分功能由其他的類(WebViewClient、WebChromeClient)來實現,這樣一來WebView主要專心干好解析、渲染工作就行了。

WebViewClient幫助WebView處理各種通知、請求事件的,具體來說包括:onLoadResource、onPageStart、onPageFinish、onReceiveError、onReceivedHttpAuthRequest;

WebChromeClient輔助WebView處理Javascript的對話框,網站圖標,網站title,加載進度。具體來說包括onCloseWindow(關閉WebView)、onCreateWindow()、onJsAlert (WebView上alert是彈不出來東西的,需要定制你的WebChromeClient處理彈出)、onJsPrompt、onJsConfirm、onProgressChanged、onReceivedIcon、onReceivedTitle;

在Android中通過 WebViewClient 的 shouldoverrideurlloading 可以捕獲到url scheme的觸發:

// WebViewClient主要幫助WebView處理各種通知、請求事件

private WebViewClient webViewClient = new WebViewClient() {

????@Override // 重寫shouldOverrideUrlLoading()方法,使得打開網頁時不調用系統瀏覽器, 而是在本WebView中顯示 public ????boolean shouldOverrideUrlLoading(WebView view, String url) {

? ? ? ? // 原生通過解析url進行相應處理

????}

};

另外,Android中也可以不通過iframe.src來觸發scheme,android中可以通過window.prompt(uri, "");來觸發scheme,然后Native中通過重寫WebViewClient的onJsPrompt來獲取uri


第四步:分析url-參數和回調的格式

Native接收到Url后,可以按照這種格式將回調參數id、api名、參數提取出來, 然后按如下步驟進行:

(1) 根據api名,在本地找尋對應的api方法, 并且記錄該方法執行完后的回調函數id

(2) 根據提取出來的參數,根據定義好的參數進行轉化

(3) 原生本地執行對應的api功能方法

(4) 功能執行完畢后,找到這次api調用對應的回調函數id,然后連同需要傳遞的參數信息,組裝成一個JSON格式的參數

JSON格式為: {

????responseId: 回調id,??

? ??responseData: 回調數據

}

?? responseId(String型)H5頁面中對應需要執行的回調函數的id,在H5中生成url scheme時就已經產生

?? responseData(JSON型) Native需要傳遞給H5的回調數據,是一個JSON格式:?{

????????code:(整型,調用是否成功,1成功,0失敗),

????????result:具體需要傳遞的結果信息,可以為任意類型,

????????msg:一些其它

}

(5)? 通過JSBridge通知H5頁面回調


第五步: Native調用JS功能實現

到了這一步,就該Native通過JSBridge調用H5的JS方法或者通知H5進行回調了,具體如下:

JSBridge._handleMessageFromNative(messageJSON);


messageJSON數據格式根據兩種不同的類型,有所區別:

Native通知H5頁面進行回調,messageJSON數據格式如下:

?{

????responseId: 回調id,??

? ??responseData: 回調數據

}

Native主動調用H5方法,messageJSON數據格式如下:

{

? ??handlerName:? 需要調用的h5 api的名稱

? ??data:? 需要傳遞的數據,固定為JSON格式

? ??callbackId:? 原生生成的回調函數id,h5執行完畢后通過url scheme通知原生api成功執行并傳遞參數

}


第六步: H5中api方法的注冊


//注冊一個測試函數

JSBridge.registerHandler('testH5Func',? function(data,? callback){

????alert('測試函數接收到數據:'+JSON.stringify(data));

????callback&&callback('測試回傳數據...');

});

data: 原生傳過來的數據;

callback: 內部封裝過一次的, 執行callback后會觸發url scheme, 通知原生獲取回調信息。


結語


自此,我們已經將JSBridge的整體架構和實現思路都講明白了,本篇文章是關于JS部分的實現。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容