摘要:作者心葉時(shí)間原理概述簡(jiǎn)介是代碼與代碼的通信橋梁。目前的一種統(tǒng)一方案是觸發(fā)捕獲原生分析執(zhí)行原生調(diào)用。另外調(diào)用時(shí)處理完畢后一定要及時(shí)通知進(jìn)行回調(diào)要不然這個(gè)回調(diào)函數(shù)不會(huì)自動(dòng)銷(xiāo)毀多了后會(huì)引發(fā)內(nèi)存泄漏。
作者:心葉
時(shí)間:2019-03-25 10:18
JSBridge是Native代碼與JS代碼的通信橋梁。目前的一種統(tǒng)一方案是:H5觸發(fā)url scheme->Native捕獲url scheme->原生分析,執(zhí)行->原生調(diào)用h5。如下圖:
url scheme介紹
上圖中有提到url scheme這個(gè)概念,那這到底是什么呢?
url scheme是一種類似于url的鏈接,是為了方便app直接互相調(diào)用設(shè)計(jì)的
具體為,可以用系統(tǒng)的OpenURI打開(kāi)一個(gè)類似于url的鏈接(可拼入?yún)?shù)),然后系統(tǒng)會(huì)進(jìn)行判斷,如果是系統(tǒng)的url scheme,則打開(kāi)系統(tǒng)應(yīng)用,否則找看是否有app注冊(cè)這種scheme,打開(kāi)對(duì)應(yīng)app
需要注意的是,這種scheme必須原生app注冊(cè)后才會(huì)生效,如微信的scheme為(weixin://)
而本文JSBridge中的url scheme則是仿照上述的形式的一種方式
具體為,app不會(huì)注冊(cè)對(duì)應(yīng)的scheme,而是由前端頁(yè)面通過(guò)某種方式觸發(fā)scheme(如用iframe.src),然后Native用某種方法捕獲對(duì)應(yīng)的url觸發(fā)事件,然后拿到當(dāng)前的觸發(fā)url,根據(jù)定義好的協(xié)議,分析當(dāng)前觸發(fā)了那種方法,然后根據(jù)定義來(lái)執(zhí)行等
注意,iOS10以后,urlscheme必須符合url規(guī)范,否則會(huì)報(bào)錯(cuò)
實(shí)現(xiàn)流程基于上述的基本原理,現(xiàn)在開(kāi)始設(shè)計(jì)一種JSBridge的實(shí)現(xiàn)
實(shí)現(xiàn)思路要實(shí)現(xiàn)JSBridge,我們可以進(jìn)行關(guān)鍵步驟分析
第一步:設(shè)計(jì)出一個(gè)Native與JS交互的全局橋?qū)ο?/p>
第二步:JS如何調(diào)用Native
第三步:Native如何得知api被調(diào)用
第四步:分析url-參數(shù)和回調(diào)的格式
第五步:Native如何調(diào)用JS
第六步:H5中api方法的注冊(cè)以及格式
如下圖:
我們規(guī)定,JS和Native之間的通信必須通過(guò)一個(gè)H5全局對(duì)象JSbridge來(lái)實(shí)現(xiàn),該對(duì)象有如下特點(diǎn)
該對(duì)象名為"JSBridge",是H5頁(yè)面中全局對(duì)象window的一個(gè)屬性
var JSBridge = window.JSBridge || (window.JSBridge = {});
該對(duì)象有如下方法
- registerHandler(String,Function) H5調(diào)用,注冊(cè)本地JS方法,注冊(cè)后Native可通過(guò)JSBridge調(diào)用。調(diào)用后會(huì)將方法注冊(cè)到本地變量messageHandlers 中 - callHandler(String,JSON,Function) H5調(diào)用,調(diào)用原生開(kāi)放的api,調(diào)用后實(shí)際上還是本地通過(guò)url scheme觸發(fā)。調(diào)用時(shí)會(huì)將回調(diào)id存放到本地變量responseCallbacks中 - _handleMessageFromNative(JSON) Native調(diào)用,原生調(diào)用H5頁(yè)面注冊(cè)的方法,或者通知H5頁(yè)面執(zhí)行回調(diào)方法
如圖
第二步:JS如何調(diào)用Native在第一步中,我們定義好了全局橋?qū)ο?可以我們是通過(guò)它的callHandler方法來(lái)調(diào)用原生的,那么它內(nèi)部經(jīng)歷了一個(gè)怎么樣的過(guò)程呢?如下:
callHandler函數(shù)內(nèi)部實(shí)現(xiàn)過(guò)程
在執(zhí)行callHandler時(shí),內(nèi)部經(jīng)歷了以下步驟:
(1)判斷是否有回調(diào)函數(shù),如果有,生成一個(gè)回調(diào)函數(shù)id,并將id和對(duì)應(yīng)回調(diào)添加進(jìn)入回調(diào)函數(shù)集合responseCallbacks中
(2)通過(guò)特定的參數(shù)轉(zhuǎn)換方法,將傳入的數(shù)據(jù),方法名一起,拼接成一個(gè)url scheme
//url scheme的格式如 //基本有用信息就是后面的callbackId,handlerName與data //原生捕獲到這個(gè)scheme后會(huì)進(jìn)行分析 var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data
(3)使用內(nèi)部早就創(chuàng)建好的一個(gè)隱藏iframe來(lái)觸發(fā)scheme
//創(chuàng)建隱藏iframe過(guò)程 var messagingIframe = document.createElement("iframe"); messagingIframe.style.display = "none"; document.documentElement.appendChild(messagingIframe); //觸發(fā)scheme messagingIframe.src = uri;
注意點(diǎn):
第三步:Native如何得知api被調(diào)用注意,正常來(lái)說(shuō)是可以通過(guò)window.location.href達(dá)到發(fā)起網(wǎng)絡(luò)請(qǐng)求的效果的,但是有一個(gè)很?chē)?yán)重的問(wèn)題,就是如果我們連續(xù)多次修改window.location.href的值,在Native層只能接收到最后一次請(qǐng)求,前面的請(qǐng)求都會(huì)被忽略掉。所以JS端發(fā)起網(wǎng)絡(luò)請(qǐng)求的時(shí)候,需要使用iframe,這樣就可以避免這個(gè)問(wèn)題。
在上一步中,我們已經(jīng)成功在H5頁(yè)面中觸發(fā)scheme,那么Native如何捕獲scheme被觸發(fā)呢?
根據(jù)系統(tǒng)不同,Android和iOS分別有自己的處理方式
Android捕獲url scheme
在Android中(WebViewClient里),通過(guò)shouldoverrideurlloading可以捕獲到url scheme的觸發(fā)
public boolean shouldOverrideUrlLoading(WebView view, String url){ //讀取到url后自行進(jìn)行分析處理 //如果返回false,則WebView處理鏈接url,如果返回true,代表WebView根據(jù)程序來(lái)執(zhí)行url return true; }
另外,Android中也可以不通過(guò)iframe.src來(lái)觸發(fā)scheme,android中可以通過(guò)window.prompt(uri, "");來(lái)觸發(fā)scheme,然后Native中通過(guò)重寫(xiě)WebViewClient的onJsPrompt來(lái)獲取uri
iOS捕獲url scheme
iOS中,UIWebView有個(gè)特性:在UIWebView內(nèi)發(fā)起的所有網(wǎng)絡(luò)請(qǐng)求,都可以通過(guò)delegate函數(shù)在Native層得到通知。這樣,我們可以在webview中捕獲url scheme的觸發(fā)(原理是利用 shouldStartLoadWithRequest)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSURL *url = [request URL]; NSString *requestString = [[request URL] absoluteString]; //獲取利潤(rùn)url scheme后自行進(jìn)行處理 return YES; }
之后Native捕獲到了JS調(diào)用的url scheme,接下來(lái)就該到下一步分析url了
第四步:分析url-參數(shù)和回調(diào)的格式在前面的步驟中,Native已經(jīng)接收到了JS調(diào)用的方法,那么接下來(lái),原生就應(yīng)該按照定義好的數(shù)據(jù)格式來(lái)解析數(shù)據(jù)了
url scheme的格式,前面已經(jīng)提到。Native接收到Url后,可以按照這種格式將回調(diào)參數(shù)id、api名、參數(shù)提取出來(lái),然后按如下步驟進(jìn)行
(1)根據(jù)api名,在本地找尋對(duì)應(yīng)的api方法,并且記錄該方法執(zhí)行完后的回調(diào)函數(shù)id
(2)根據(jù)提取出來(lái)的參數(shù),根據(jù)定義好的參數(shù)進(jìn)行轉(zhuǎn)化
如果是JSON格式需要手動(dòng)轉(zhuǎn)換,如果是String格式直接可以使用
(3)原生本地執(zhí)行對(duì)應(yīng)的api功能方法
(4)功能執(zhí)行完畢后,找到這次api調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)id,然后連同需要傳遞的參數(shù)信息,組裝成一個(gè)JSON格式的參數(shù)
回調(diào)的JSON格式為:{responseId:回調(diào)id,responseData:回調(diào)數(shù)據(jù)}
responseId String型 H5頁(yè)面中對(duì)應(yīng)需要執(zhí)行的回調(diào)函數(shù)的id,在H5中生成url scheme時(shí)就已經(jīng)產(chǎn)生
responseData JSON型 Native需要傳遞給H5的回調(diào)數(shù)據(jù),是一個(gè)JSON格式: {code:(整型,調(diào)用是否成功,1成功,0失敗),result:具體需要傳遞的結(jié)果信息,可以為任意類型,msg:一些其它信息,如調(diào)用錯(cuò)誤時(shí)的錯(cuò)誤信息}
(5)通過(guò)JSBridge通知H5頁(yè)面回調(diào)
參考 第五步Native如何調(diào)用JS
第五步:Native如何調(diào)用JS到了這一步,就該Native通過(guò)JSBridge調(diào)用H5的JS方法或者通知H5進(jìn)行回調(diào)了,具體如下
//將回調(diào)信息傳給H5 JSBridge._handleMessageFromNative(messageJSON);
如上,實(shí)際上是通過(guò)JSBridge的_handleMessageFromNative傳遞數(shù)據(jù)給H5,其中的messageJSON數(shù)據(jù)格式根據(jù)兩種不同的類型,有所區(qū)別,如下
Native通知H5頁(yè)面進(jìn)行回調(diào)
數(shù)據(jù)格式為:上文中的回調(diào)的JSON格式
Native主動(dòng)調(diào)用H5方法
Native主動(dòng)調(diào)用H5方法時(shí),數(shù)據(jù)格式是:{handlerName:api名,data:數(shù)據(jù),callbackId:回調(diào)id}
handlerName String型 需要調(diào)用的,h5中開(kāi)放的api的名稱
data JSON型 需要傳遞的數(shù)據(jù),固定為JSON格式(因?yàn)槲覀児潭℉5中注冊(cè)的方法接收的第一個(gè)參數(shù)必須是JSON,第二個(gè)是回調(diào)函數(shù))
注意,這一步中,如果Native調(diào)用的api是h5沒(méi)有注冊(cè)的,h5頁(yè)面上會(huì)有對(duì)應(yīng)的錯(cuò)誤提示。
另外,H5調(diào)用Native時(shí),Native處理完畢后一定要及時(shí)通知H5進(jìn)行回調(diào),要不然這個(gè)回調(diào)函數(shù)不會(huì)自動(dòng)銷(xiāo)毀,多了后會(huì)引發(fā)內(nèi)存泄漏。
第六步:H5中api方法的注冊(cè)以及格式前面有提到Native主動(dòng)調(diào)用H5中注冊(cè)的api方法,那么h5中怎么注冊(cè)供原生調(diào)用的api方法呢?格式又是什么呢?如下
H5中注冊(cè)供原生調(diào)用的API
//注冊(cè)一個(gè)測(cè)試函數(shù) JSBridge.registerHandler("testH5Func",function(data,callback){ alert("測(cè)試函數(shù)接收到數(shù)據(jù):"+JSON.stringify(data)); callback&&callback("測(cè)試回傳數(shù)據(jù)..."); });
如上述代碼為注冊(cè)一個(gè)供原生調(diào)用的api
H5中注冊(cè)的API格式注意
如上代碼,注冊(cè)的api參數(shù)是(data,callback)
其中第一個(gè)data即原生傳過(guò)來(lái)的數(shù)據(jù),第二個(gè)callback是內(nèi)部封裝過(guò)一次的,執(zhí)行callback后會(huì)觸發(fā)url scheme,通知原生獲取回調(diào)信息
思路
大致思路就是
h5調(diào)用Native的關(guān)鍵步驟進(jìn)行拆分,由以前的直接傳遞url scheme變?yōu)閭鬟f一個(gè)統(tǒng)一的url scheme,然后Native主動(dòng)獲取傳遞的參數(shù)
完善以前: H5調(diào)用Native->將所有參數(shù)組裝成為url scheme->原生捕獲scheme,進(jìn)行分析
完善以后: H5調(diào)用Native->將所有參數(shù)存入本地?cái)?shù)組->觸發(fā)一個(gè)固定的url scheme->原生捕獲scheme->原生通過(guò)JSBridge主動(dòng)獲取參數(shù)->進(jìn)行分析
實(shí)現(xiàn)
這種完善后的流程和以前有所區(qū)別,如下
JSBridge對(duì)象圖解
JSBridge實(shí)現(xiàn)完整流程
注意
由于這次完善的核心是:Native主動(dòng)調(diào)用JS函數(shù),并獲取返回值。而在Android4.4以前,Android是沒(méi)有這個(gè)功能的,所以并不完全適用于Android
所以一般會(huì)進(jìn)行一個(gè)兼容處理,Android中采用以前的scheme傳法,iOS使用完善后的方案(也便于4.4普及后后續(xù)的完善)
完整的JSBridge上述分析了JSBridge的實(shí)現(xiàn)流程,那么實(shí)際項(xiàng)目中,我們就應(yīng)該結(jié)合上述兩種,針對(duì)Android和iOS的不同情況,統(tǒng)一出一種完整的方案,如下
完整調(diào)用流程圖 例子基于上面的思想,個(gè)人在github上維護(hù)了一個(gè)用于學(xué)習(xí)的項(xiàng)目(非轉(zhuǎn)載內(nèi)容):https://github.com/yelloxing/...不采用url scheme方式
前面提到的JSBridge都是基于url scheme的,但其實(shí)如果不考慮Android4.2以下,iOS7以下,其實(shí)也可以用另一套方案的,如下:
Native調(diào)用JS的方法不變
JS調(diào)用Native是不再通過(guò)觸發(fā)url scheme,而是采用自帶的交互,比如
Android中,原生通過(guò) addJavascriptInterface開(kāi)放一個(gè)統(tǒng)一的api給JS調(diào)用,然后將觸發(fā)url scheme步驟變?yōu)檎{(diào)用這個(gè)api,其余步驟不變(相當(dāng)于以前是url接收參數(shù),現(xiàn)在變?yōu)閍pi函數(shù)接收參數(shù))
iOS中,原生通過(guò)JavaScriptCore里面的方法來(lái)注冊(cè)一個(gè)統(tǒng)一api,其余和Android中一樣(這里就不需要主動(dòng)獲取參數(shù)了,因?yàn)閰?shù)可以直接由這個(gè)函數(shù)統(tǒng)一接收)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102932.html
摘要:一原理篇下面分別介紹和與的底層交互原理在講解原理之前,首先來(lái)了解下的組件,先來(lái)看一下蘋(píng)果官方的介紹上面的意思是說(shuō)是一個(gè)可加載網(wǎng)頁(yè)的對(duì)象,它有瀏覽記錄功能,且對(duì)加載的網(wǎng)頁(yè)內(nèi)容是可編程的。 做過(guò)混合開(kāi)發(fā)的很多人都知道Ionic和PhoneGap之類的框架,這些框架在web基礎(chǔ)上包了一層Native,然后通過(guò)Bridge技術(shù)使得js可以調(diào)用視頻、位置、音頻等功能。本文就是介紹這層Bridge...
閱讀 1687·2019-08-30 15:55
閱讀 997·2019-08-30 15:44
閱讀 892·2019-08-30 10:48
閱讀 2064·2019-08-29 13:42
閱讀 3206·2019-08-29 11:16
閱讀 1336·2019-08-29 11:09
閱讀 2079·2019-08-26 11:46
閱讀 636·2019-08-26 11:44