天天看點

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

做過混合開發的人都知道ionic和phonegap之類的架構,這些架構在web基礎上包裝一層native,然後通過bridge技術的js調用本地的庫。

在講jsbridge技術之前,我們來看一下傳統的實作方式。

native調用js比較簡單,隻要遵循:"javascript: 方法名('參數,需要轉為字元串')"的規則即可。

在4.4之前,調用的方式:

4.4以後(包括4.4),使用以下方式:

說明:

4.4之前native通過loadurl來調用js方法,隻能讓某個js方法執行,但是無法擷取該方法的傳回值

4.4之後,通過evaluatejavascript異步調用js方法,并且能在onreceivevalue中拿到傳回值

不适合傳輸大量資料(大量資料建議用接口方式擷取)

mwebview.loadurl("javascript: 方法名('參數,需要轉為字元串')");函數需在ui線程運作,因為mwebview為ui控件

js調用native需要對webview設定@javascriptinterface注解,這裡有個漏洞,後面會給大家說明。要想js能夠native,需要對webview設定以下屬性。

這裡我們看到了getjsbridge(),native中通過addjavascriptinterface添加暴露出來的js橋對象,然後再該對象内部聲明對應的api方法。

那麼html怎麼調用native的方法呢?

在android4.2以上(api17後),暴露的api要加上注解@javascriptinterface,否則會找不到方法。

在api17以前,addjavascriptinterface有風險,hacker可以通過反編譯擷取native注冊的js對象,然後在頁面通過反射java的内置 靜态類,擷取一些敏感的資訊和破壞

js調用native暴露的api,并且能得到相應傳回值

native調用js的方法比較簡單,native通過stringbyevaluatingjavascriptfromstring調用html綁定在window上的函數。不過應注意oc和swift的寫法。

native調用js方法時,能拿到js方法的傳回值

native中通過引入官方提供的javascriptcore庫(ios7以上),然後可以将api綁定到jscontext上(然後html中js預設通過window.top.*可調用)。

引入官方的庫檔案

native注冊api函數(oc)

html中js調用native方法

ios7才出現這種方式,在這之前,js無法直接調用native,隻能通過jsbridge方式簡介調用

js能調用到已經暴露的api,并且能得到相應傳回值

ios原生本身是無法被js調用的,但是通過引入官方提供的第三方"javascriptcore",即可開放api給js調用

jsbridge:聽其取名就是js和native之前的橋梁,而實際上jsbridge确實是js和native之前的一種通信方式。簡單的說,jsbridge就是定義native和js的通信,native隻通過一個固定的橋對象調用js,js也隻通過固定的橋對象調用native。jsbridge另一個叫法及大家熟知的hybrid app技術。

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

流程:h5->通過某種方式觸發一個url->native捕獲到url,進行分析->原生做處理->native調用h5的jsbridge對象傳遞回調。

我們前面講過了原生的webview/uiwebview控件已經能夠和js實作資料通信了,那為什麼還要jsbridge呢?

其實使用jsbridge有很多方面的考慮:

android4.2以下,addjavascriptinterface方式有安全漏掉

ios7以下,js無法調用native

url scheme互動方式是一套現有的成熟方案,可以完美相容各種版本,對以前老版本技術的相容。

url scheme是一種類似于url的連結,是為了友善app直接互相調用設計的。具體來講如果是系統的url scheme,則打開系統應用,否則找看是否有app注冊這種scheme,打開對應app。

注:這種scheme必須原生app注冊後才會生效。

而在我們實際的開發中,app不會注冊對應的scheme,而是由前端頁面通過某種方式觸發scheme(如用iframe.src),然後native用某種方法捕獲對應的url觸發事件,然後拿到目前的觸發url,根據定義好的協定,分析目前觸發了那種方法。

要實作jsbridge,我們需要按以下步驟分析:

第一步:設計出一個native與js互動的全局橋對象

第二步:js如何調用native

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

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

第五步:native如何調用js

第六步:h5中api方法的注冊以及格式

jsbridge的完整流程可總結為:

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

我們規定,js和native之間的通信必須通過一個h5全局對象jsbridge來實作。該對象有如下特點:

該對象名為"jsbridge",是h5頁面中全局對象window的一個屬性,形如:

該對象有如下方法:

registerhandler( string,function )h5調用注冊本地js方法,注冊後native可通過jsbridge調用。調用後會将方法注冊到本地變量messagehandlers 中。

callhandler( string,json,function )h5調用 調用原生開放的api,調用後實際上還是本地通過url scheme觸發。調用時會将回調id存放到本地變量responsecallbacks中

_handlemessagefromnative( json )native調用 原生調用h5頁面注冊的方法,或者通知h5頁面執行回調方法

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

我們定義好了全局橋對象,可以通過它的callhandler方法來調用原生的api。

在執行callhandler時,内部經曆了以下步驟:

判斷是否有回調函數,如果有,生成一個回調函數id,并将id和對應回調添加進入回調函數集合responsecallbacks中。

通過特定的參數轉換方法,将傳入的資料,方法名一起,拼接成一個url scheme

使用内部早就建立好的一個隐藏iframe來觸發scheme

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

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

根據系統不同,android和ios分别有自己的處理方式。

在android中(webviewclient裡),通過shouldoverrideurlloading可以捕獲到url scheme的觸發。

ios中,uiwebview有個特性:在uiwebview内發起的所有網絡請求,都可以通過delegate函數在native層得到通知。這樣,我們可以在webview中捕獲url scheme的觸發(原理是利用 shouldstartloadwithrequest)

在前面的步驟中,native已經接收到了js調用的方法,那麼接下來,原生就應該按照定義好的資料格式來解析資料了,native接收到url後,可以按照這種格式将回調參數id、api名、參數提取出來,然後按如下步驟進行。

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

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

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

功能執行完畢後,找到這次api調用對應的回調函數id,然後連同需要傳遞的參數資訊,組裝成一個json格式的參數

通過jsbridge通知h5頁面回調

到了這一步,就該native通過jsbridge調用h5的js方法或者通知h5進行回調了。其中的messagejson資料格式根據兩種不同的類型。

native通知h5頁面進行回調:

資料格式為: native通知h5回調的json格式。

native主動調用h5方法:

native主動調用h5方法時,資料格式是:{handlername:api名,data:資料,callbackid:回調id}:

handlername string型 需要調用的,h5中開放的api的名稱

data json型 需要傳遞的資料,固定為json格式(因為我們固定h5中注冊的方法接收的第一個參數必須是json,第二個是回調函數)

callbackid string型 原生生成的回調函數id,h5執行完畢後通過url scheme通知原生api成功執行,并傳遞參數

前面有提到native主動調用h5中注冊的api方法,那麼h5中怎麼注冊供原生調用的api方法呢?

如上代碼,其中第一個data即原生傳過來的資料,第二個callback是内部封裝過一次的,執行callback後會觸發url scheme,通知原生擷取回調資訊.

jsbridge對象圖解:

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

jsbridge實作完整流程:

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

那麼我們在實際的開發中,如何針對android和ios的不同情況,統一出一種完整的方案。

JSBridge深度剖析概述JSBridgeJSBridge技術實作總結

前面提到的jsbridge都是基于url scheme的,但其實如果不考慮android4.2以下,ios7以下,其實也可以用另一套方案的。

native調用js的方法不變

js調用native是不再通過觸發url scheme,而是采用自帶的互動

具體來講:

android中,原生通過 addjavascriptinterface開放一個統一的api給js調用,然後将觸發url scheme步驟變為調用這個api,其餘步驟不變。

os中,原生通過javascriptcore裡面的方法來注冊一個統一api,其餘和android中一樣。

繼續閱讀