(轉)iOS- JSBridge的原理

做者:心葉
時間:2019-03-25 10:18前端

原理概述

簡介

JSBridge是Native代碼與JS代碼的通訊橋樑。目前的一種統一方案是:H5觸發url scheme->Native捕獲url scheme->原生分析,執行->原生調用h5。以下圖:android

圖片描述

url scheme介紹git

上圖中有提到url scheme這個概念,那這究竟是什麼呢?github

  • url scheme是一種相似於url的連接,是爲了方便app直接互相調用設計的web

    • 具體爲,能夠用系統的OpenURI打開一個相似於url的連接(可拼入參數),而後系統會進行判斷,若是是系統的url scheme,則打開系統應用,不然找看是否有app註冊這種scheme,打開對應app
    • 須要注意的是,這種scheme必須原生app註冊後纔會生效,如微信的scheme爲(weixin://)
  • 而本文JSBridge中的url scheme則是仿照上述的形式的一種方式api

    • 具體爲,app不會註冊對應的scheme,而是由前端頁面經過某種方式觸發scheme(如用iframe.src),而後Native用某種方法捕獲對應的url觸發事件,而後拿到當前的觸發url,根據定義好的協議,分析當前觸發了那種方法,而後根據定義來執行等
  • 注意,iOS10之後,urlscheme必須符合url規範,不然會報錯

實現流程

基於上述的基本原理,如今開始設計一種JSBridge的實現數組

實現思路

要實現JSBridge,咱們能夠進行關鍵步驟分析微信

  • 第一步:設計出一個Native與JS交互的全局橋對象
  • 第二步:JS如何調用Native
  • 第三步:Native如何得知api被調用
  • 第四步:分析url-參數和回調的格式
  • 第五步:Native如何調用JS
  • 第六步:H5中api方法的註冊以及格式

以下圖:
圖片描述網絡

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

咱們規定,JS和Native之間的通訊必須經過一個H5全局對象JSbridge來實現,該對象有以下特色app

  • 該對象名爲"JSBridge",是H5頁面中全局對象window的一個屬性
var JSBridge = window.JSBridge || (window.JSBridge = {});
  • 該對象有以下方法

    - registerHandler(String,Function) H5調用,註冊本地JS方法,註冊後Native可經過JSBridge調用。調用後會將方法註冊到本地變量messageHandlers 中
    
    - callHandler(String,JSON,Function) H5調用,調用原生開放的api,調用後實際上仍是本地經過url scheme觸發。調用時會將回調id存放到本地變量responseCallbacks中
    
    - _handleMessageFromNative(JSON) Native調用,原生調用H5頁面註冊的方法,或者通知H5頁面執行回調方法
  • 如圖

圖片描述

第二步:JS如何調用Native

在第一步中,咱們定義好了全局橋對象,能夠咱們是經過它的callHandler方法來調用原生的,那麼它內部經歷了一個怎麼樣的過程呢?以下:

callHandler函數內部實現過程

在執行callHandler時,內部經歷瞭如下步驟:

  • (1)判斷是否有回調函數,若是有,生成一個回調函數id,並將id和對應回調添加進入回調函數集合responseCallbacks中
  • (2)經過特定的參數轉換方法,將傳入的數據,方法名一塊兒,拼接成一個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;

注意點

注意,正常來講是能夠經過window.location.href達到發起網絡請求的效果的,可是有一個很嚴重的問題,就是若是咱們連續屢次修改window.location.href的值,在Native層只能接收到最後一次請求,前面的請求都會被忽略掉。因此JS端發起網絡請求的時候,須要使用iframe,這樣就能夠避免這個問題。

第三步:Native如何得知api被調用

在上一步中,咱們已經成功在H5頁面中觸發scheme,那麼Native如何捕獲scheme被觸發呢?

根據系統不一樣,Android和iOS分別有本身的處理方式

Android捕獲url scheme

在Android中(WebViewClient裏),經過shouldoverrideurlloading能夠捕獲到url scheme的觸發

public boolean shouldOverrideUrlLoading(WebView view, String url){
    //讀取到url後自行進行分析處理

    //若是返回false,則WebView處理連接url,若是返回true,表明WebView根據程序來執行url
    return true;
}

另外,Android中也能夠不經過iframe.src來觸發scheme,android中能夠經過window.prompt(uri, "");來觸發scheme,而後Native中經過重寫WebViewClient的onJsPrompt來獲取uri

iOS捕獲url scheme

iOS中,UIWebView有個特性:在UIWebView內發起的全部網絡請求,均可以經過delegate函數在Native層獲得通知。這樣,咱們能夠在webview中捕獲url scheme的觸發(原理是利用 shouldStartLoadWithRequest)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];

    NSString *requestString = [[request URL] absoluteString];
    //獲取利潤url scheme後自行進行處理
   return YES;
}

以後Native捕獲到了JS調用的url scheme,接下來就該到下一步分析url了

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

在前面的步驟中,Native已經接收到了JS調用的方法,那麼接下來,原生就應該按照定義好的數據格式來解析數據了

url scheme的格式,前面已經提到。Native接收到Url後,能夠按照這種格式將回調參數id、api名、參數提取出來,而後按以下步驟進行

  • (1)根據api名,在本地找尋對應的api方法,而且記錄該方法執行完後的回調函數id
  • (2)根據提取出來的參數,根據定義好的參數進行轉化

    • 若是是JSON格式須要手動轉換,若是是String格式直接可使用
  • (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如何調用JS

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

//將回調信息傳給H5
JSBridge._handleMessageFromNative(messageJSON);

如上,其實是經過JSBridge的_handleMessageFromNative傳遞數據給H5,其中的messageJSON數據格式根據兩種不一樣的類型,有所區別,以下

Native通知H5頁面進行回調

數據格式爲:上文中的回調的JSON格式

Native主動調用H5方法

Native主動調用H5方法時,數據格式是:{handlerName:api名,data:數據,callbackId:回調id}

  • handlerName String型 須要調用的,h5中開放的api的名稱
  • data JSON型 須要傳遞的數據,固定爲JSON格式(由於咱們固定H5中註冊的方法接收的第一個參數必須是JSON,第二個是回調函數)

注意,這一步中,若是Native調用的api是h5沒有註冊的,h5頁面上會有對應的錯誤提示。

另外,H5調用Native時,Native處理完畢後必定要及時通知H5進行回調,要否則這個回調函數不會自動銷燬,多了後會引起內存泄漏。

第六步:H5中api方法的註冊以及格式

前面有提到Native主動調用H5中註冊的api方法,那麼h5中怎麼註冊供原生調用的api方法呢?格式又是什麼呢?以下

H5中註冊供原生調用的API

//註冊一個測試函數
JSBridge.registerHandler('testH5Func',function(data,callback){
    alert('測試函數接收到數據:'+JSON.stringify(data));
    callback&&callback('測試回傳數據...');
});

如上述代碼爲註冊一個供原生調用的api

H5中註冊的API格式注意

如上代碼,註冊的api參數是(data,callback)

其中第一個data即原生傳過來的數據,第二個callback是內部封裝過一次的,執行callback後會觸發url scheme,通知原生獲取回調信息

思路

大體思路就是

  • h5調用Native的關鍵步驟進行拆分,由之前的直接傳遞url scheme變爲傳遞一個統一的url scheme,而後Native主動獲取傳遞的參數

    • 完善之前: H5調用Native->將全部參數組裝成爲url scheme->原生捕獲scheme,進行分析
    • 完善之後: H5調用Native->將全部參數存入本地數組->觸發一個固定的url scheme->原生捕獲scheme->原生經過JSBridge主動獲取參數->進行分析

實現

這種完善後的流程和之前有所區別,以下

JSBridge對象圖解

圖片描述

JSBridge實現完整流程

圖片描述

注意
因爲此次完善的核心是:Native主動調用JS函數,並獲取返回值。而在Android4.4之前,Android是沒有這個功能的,因此並不徹底適用於Android

因此通常會進行一個兼容處理,Android中採用之前的scheme傳法,iOS使用完善後的方案(也便於4.4普及後後續的完善)

完整的JSBridge

上述分析了JSBridge的實現流程,那麼實際項目中,咱們就應該結合上述兩種,針對Android和iOS的不一樣狀況,統一出一種完整的方案,以下

完整調用流程圖

圖片描述

例子

基於上面的思想,我的在github上維護了一個用於學習的項目(非轉載內容): https://github.com/yelloxing/...

不採用url scheme方式

前面提到的JSBridge都是基於url scheme的,但其實若是不考慮Android4.2如下,iOS7如下,其實也能夠用另外一套方案的,以下:

  • Native調用JS的方法不變
  • JS調用Native是再也不經過觸發url scheme,而是採用自帶的交互,好比
  • Android中,原生經過 addJavascriptInterface開放一個統一的api給JS調用,而後將觸發url scheme步驟變爲調用這個api,其他步驟不變(至關於之前是url接收參數,如今變爲api函數接收參數)
  • iOS中,原生經過JavaScriptCore裏面的方法來註冊一個統一api,其他和Android中同樣(這裏就不須要主動獲取參數了,由於參數能夠直接由這個函數統一接收)
相關文章
相關標籤/搜索