前言
本文主要內(nèi)容是對(duì)iOS的WebViewJavascriptBridge源碼進(jìn)行分析梳理蒜田,分為iOS端調(diào)用JS端,以及JS端調(diào)用iOS端兩部分,并在最后進(jìn)行了總結(jié)逼蒙。
iOS端調(diào)用JS端
JS端調(diào)用
WebViewJavascriptBridge
的registerHandler(handlerName, handler)
注冊(cè)相應(yīng)的回調(diào)澈蝙,并將handler
存到一個(gè)messageHandlers
的數(shù)組當(dāng)中吓坚。JS端通過將一個(gè)隱藏的
iframe
的src
設(shè)置為:https://__bridge_loaded__
,來觸發(fā)OC的WebView的回調(diào)webView: shouldStartLoadWithRequest: navigationType:
碉克,此時(shí)會(huì)判斷該請(qǐng)求的url凌唬,如果是上面的的https://__bridge_loaded__
,則將WebViewJavascriptBridge
的JS代碼注入(通過調(diào)用UIWebView的stringByEvaluatingJavaScriptFromString:
或者WKWebview的stringByEvaluatingJavaScriptFromString:completionHandler:
的方法)到網(wǎng)頁(yè)中漏麦。-
iOS端調(diào)用
callHandler:handlerName data:responseCallback:
客税,并將其參數(shù)封裝成一個(gè)WVJBMessage(NSDictionary)并將responseCallback
存到responseCallbacks(NSMutableDictionary)
中,格式如下:message: { callbackId = "objc_cb_6"; //6是_uniqueId從0撕贞,以1為步長(zhǎng)累加上來的 data = { greetingFromObjC = "Hi there, JS!"; }; handlerName = testJavascriptHandler; //在js端注冊(cè)的名字更耻,其對(duì)應(yīng)的是一個(gè)方法。 }
- 向Web端注入JSBridge的JS代碼之前先將message保存在一個(gè)
startupMessageQueue(NSMutableArray)
中捏膨,待調(diào)用injectJavascriptFile
向Web端注入JSBridge的JS的時(shí)候秧均,遞歸的執(zhí)行_dispatchMessage:
。 - iOS端調(diào)用方法
_dispatchMessage:
中將message序列化成一個(gè)json字符串messageJSON
号涯。
- 向Web端注入JSBridge的JS代碼之前先將message保存在一個(gè)
iOS端接下來會(huì)回調(diào)UIWebView的
stringByEvaluatingJavaScriptFromString:
或者WKWebview的stringByEvaluatingJavaScriptFromString:completionHandler:
方法執(zhí)行JS端的代碼:WebViewJavascriptBridge._handleMessageFromObjC('messageJSON')
JS端執(zhí)行
WebViewJavascriptBridge._handleMessageFromObjC()
,從數(shù)組messageHandlers
根據(jù)上面所說的handlerName
取出相應(yīng)的方法執(zhí)行目胡,如果iOS端有傳入callbackId
,則將其變成responseId
链快,和要返回的數(shù)據(jù)responseData
誉己,構(gòu)造一個(gè)message
,并寫入在sendMessageQueue
中域蜗,接著將iframe
的src
設(shè)置為:https://__wvjb_queue_message__
巨双,這將觸發(fā)UIWebView的webView:shouldStartLoadWithRequest:navigationType:
或者WKWebView的webView:decidePolicyForNavigationAction:decisionHandler:
回調(diào)方法噪猾。iOS端判斷請(qǐng)求是否是
https://__wvjb_queue_message__
,如果是筑累,則調(diào)用flushMessageQueue
袱蜡,從JS端中的sendMessageQueue
取出message(json格式)
,并將其轉(zhuǎn)換成本地的WVJBMessage(NSDictionary)
慢宗,并通過它的responseId
從本地的responseCallbacks
取出回調(diào)方法進(jìn)行執(zhí)行坪蚁。
JS端調(diào)用iOS端
- iOS端在WebView加載前,調(diào)用
registerHandler:handler:
方法注冊(cè)回調(diào)婆廊。這些回調(diào)會(huì)以注冊(cè)時(shí)傳入的方法名為key迅细,存入名為messageHandlers(NSMutableDictionary)
中。 - JS端調(diào)用
setupWebViewJavascriptBridge:
發(fā)起一個(gè)https://__bridge_loaded__
請(qǐng)求(通過設(shè)置iframe
的src
實(shí)現(xiàn))觸發(fā)iOS端的UIWebView的webView:shouldStartLoadWithRequest:navigationType:
或者WKWebView的webView:decidePolicyForNavigationAction:decisionHandler:
回調(diào)方法淘邻,將JSBridge的JS代碼注入到網(wǎng)頁(yè)中茵典。 - JS端調(diào)用
bridge.callHandler(handlerName, data, responseCallback)
,然后將各個(gè)參數(shù)封裝成一個(gè)message宾舅,其中會(huì)生成一個(gè)callbackId
來標(biāo)記這個(gè)responseCallback
统阿,存入sendMessageQueue
中,接著設(shè)置iframe
的src
為https://__wvjb_queue_message__
筹我,依次觸發(fā)UIWebView的webView:shouldStartLoadWithRequest:navigationType:
或者WKWebView的webView:decidePolicyForNavigationAction:decisionHandler:
回調(diào)方法扶平。 - iOS端判斷請(qǐng)求是否是
https://__wvjb_queue_message__
,如果是蔬蕊,則調(diào)用flushMessageQueue
结澄,從JS端中的sendMessageQueue
取出message(json格式)
,并將其轉(zhuǎn)換成本地的WVJBMessage(NSDictionary)
岸夯,并通過它的handlerName
從本地的messageHandlers
取出回調(diào)方法進(jìn)行執(zhí)行麻献。 - iOS端接著將JS端傳入的
callbackId
變成responseId
并和responseData
一起構(gòu)造一個(gè)message
,然后調(diào)用方法_dispatchMessage:
中將message
序列化成一個(gè)json字符串messageJSON
。 - JS端執(zhí)行
WebViewJavascriptBridge._handleMessageFromObjC()
,根據(jù)responseId
從數(shù)組responseCallbacks
中取出相應(yīng)的回調(diào)執(zhí)行猜扮。
總結(jié)
通過上面的分析勉吻,我們可以發(fā)現(xiàn):
iOS端調(diào)用JS接口,通過UIWebView的
stringByEvaluatingJavaScriptFromString:
或者WKWebview的stringByEvaluatingJavaScriptFromString:completionHandler:
的方法既可旅赢。但是為了取得取得JS返回的結(jié)果齿桃,JS必須將結(jié)果封裝成一個(gè)json數(shù)據(jù),并寫入的sendMessageQueue
煮盼,然后通過設(shè)置一個(gè)隱藏的iframe
的src
為https://__wvjb_queue_message__
來觸發(fā)iOS端的WebView的回調(diào)短纵,接著就在回調(diào)中調(diào)用JS的WebViewJavascriptBridge._fetchQueue()
方法,取回要返回的結(jié)果信息僵控。JS端調(diào)用iOS端的接口香到,則先將消息寫入
sendMessageQueue
中,然后通過設(shè)置一個(gè)隱藏的iframe
的src
為https://__wvjb_queue_message__
來觸發(fā)iOS端的WebView的回調(diào),接著就在回調(diào)中調(diào)用JS的WebViewJavascriptBridge._fetchQueue()
方法养渴,取回要執(zhí)行的方法的信息,iOS端根據(jù)這些信息找到相應(yīng)的block
進(jìn)行執(zhí)行泛烙,最后將返回的結(jié)果轉(zhuǎn)換成json理卑,并調(diào)用JS的WebViewJavascriptBridge._handleMessageFromObjC(messageJson)
方法將結(jié)果回傳給JS。