摘要:作者心葉時間原理概述簡介是代碼與代碼的通信橋梁。目前的一種統一方案是觸發捕獲原生分析執行原生調用。另外調用時處理完畢后一定要及時通知進行回調要不然這個回調函數不會自動銷毀多了后會引發內存泄漏。
作者:心葉
時間:2019-03-25 10:18
JSBridge是Native代碼與JS代碼的通信橋梁。目前的一種統一方案是:H5觸發url scheme->Native捕獲url scheme->原生分析,執行->原生調用h5。如下圖:
url scheme介紹
上圖中有提到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,根據定義好的協議,分析當前觸發了那種方法,然后根據定義來執行等
注意,iOS10以后,urlscheme必須符合url規范,否則會報錯
實現流程基于上述的基本原理,現在開始設計一種JSBridge的實現
實現思路要實現JSBridge,我們可以進行關鍵步驟分析
第一步:設計出一個Native與JS交互的全局橋對象
第二步:JS如何調用Native
第三步:Native如何得知api被調用
第四步:分析url-參數和回調的格式
第五步:Native如何調用JS
第六步:H5中api方法的注冊以及格式
如下圖:
我們規定,JS和Native之間的通信必須通過一個H5全局對象JSbridge來實現,該對象有如下特點
該對象名為"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;
注意點:
第三步:Native如何得知api被調用注意,正常來說是可以通過window.location.href達到發起網絡請求的效果的,但是有一個很嚴重的問題,就是如果我們連續多次修改window.location.href的值,在Native層只能接收到最后一次請求,前面的請求都會被忽略掉。所以JS端發起網絡請求的時候,需要使用iframe,這樣就可以避免這個問題。
在上一步中,我們已經成功在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中一樣(這里就不需要主動獲取參數了,因為參數可以直接由這個函數統一接收)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102932.html
摘要:一原理篇下面分別介紹和與的底層交互原理在講解原理之前,首先來了解下的組件,先來看一下蘋果官方的介紹上面的意思是說是一個可加載網頁的對象,它有瀏覽記錄功能,且對加載的網頁內容是可編程的。 做過混合開發的很多人都知道Ionic和PhoneGap之類的框架,這些框架在web基礎上包了一層Native,然后通過Bridge技術使得js可以調用視頻、位置、音頻等功能。本文就是介紹這層Bridge...
閱讀 1661·2019-08-30 15:55
閱讀 982·2019-08-30 15:44
閱讀 874·2019-08-30 10:48
閱讀 2048·2019-08-29 13:42
閱讀 3191·2019-08-29 11:16
閱讀 1272·2019-08-29 11:09
閱讀 2060·2019-08-26 11:46
閱讀 622·2019-08-26 11:44