前序
本文csdn地址:http://blog.csdn.net/game3108/article/details/51147923
iOS原生應(yīng)用和web頁(yè)面的交互主要有:JavaScriptCore(iOS7以后)與攔截協(xié)議兩個(gè)方法照卦。
因?yàn)槲覀兊腶pp要兼容iOS6,所以我們?cè)趙eb js和native交互使用的是攔截協(xié)議的一個(gè)很有名的第三方框架:WebViewJavascriptBridge珊燎,本文從源代碼來(lái)解析一下WebViewJavascriptBridge的工作方式享言。
(對(duì)于iOS8新出的WKWebView,原理方式相同,會(huì)稍作提及拓巧,但不會(huì)詳細(xì)展開(kāi)环壤。)
iOS web和native交互的方式
首先拋開(kāi)WebViewJavascriptBridge,思考一個(gè)問(wèn)題,如果我們自己去做一套js與native交互的輪子诉濒,應(yīng)該如何去做周伦?
- 尋找js與native可能交互的接口
- 設(shè)計(jì)一套融合交互接口的數(shù)據(jù)模式
- 完善整體輪子
我們來(lái)依此分析每一項(xiàng)
尋找js與native可能交互的接口
查詢相應(yīng)的文檔可以得知,在UIWebView中未荒,native有直接調(diào)用js的方法,js沒(méi)有直接調(diào)用native的方法
native直接調(diào)用js的方法:
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;//WKWebView使用专挪,以下類推)
那對(duì)于js來(lái)說(shuō),無(wú)法直接調(diào)用native代碼是否表示無(wú)法進(jìn)行交互片排?
答案是否定的寨腔。雖然無(wú)法直接調(diào)用native代碼,但是iOS的接口中還是設(shè)計(jì)了可以通過(guò)間接的方式傳遞js調(diào)用的消息
js間接調(diào)用native的方式:
對(duì)于iOS UIWebView熟悉的人來(lái)說(shuō)率寡,
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
這個(gè)方法迫卢,可以每次在UIWebView進(jìn)行重定向URL的時(shí)候,進(jìn)行觸發(fā)冶共,只要把一個(gè)js調(diào)用native的方法包裝成一個(gè)重定向URL,就可以在本地接收到相應(yīng)的方法乾蛤。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
小結(jié):
native可以直接調(diào)用js,js將方法包裝成重定向請(qǐng)求,使得native截取并分析捅僵,執(zhí)行相應(yīng)代碼家卖。
問(wèn)題:那就是說(shuō),js代碼每次把一次調(diào)用里面所有參數(shù)都集合進(jìn)一個(gè)重定向請(qǐng)求里直接讓本地截取和執(zhí)行庙楚?
設(shè)計(jì)一套融合交互接口的數(shù)據(jù)模式
在js與native交互的時(shí)候上荡,設(shè)計(jì)一套兩邊都可以使用的模式十分關(guān)鍵。
native代碼與交互js代碼明顯是異步的一個(gè)操作醋奠,尤其是在雙方初始化的時(shí)候榛臼,native代碼無(wú)法確定js代碼是否加載完成伊佃,如果在js未加載完成的時(shí)候進(jìn)行相應(yīng)方法調(diào)用,是沒(méi)有效果的沛善。
所以在雙方交互的時(shí)候航揉,設(shè)計(jì)一個(gè)list數(shù)組,去存儲(chǔ)兩遍在未初始化完成前需要執(zhí)行的方法金刁,十分重要帅涂。
而對(duì)于每一個(gè)方法請(qǐng)求,我們至少需要以下參數(shù):
- 方法名
- 方法參數(shù)
- 回調(diào)
在此基礎(chǔ)上尤蛮,key-value對(duì)(Dictionary)會(huì)是一個(gè)很好的選擇媳友。
小結(jié):交互設(shè)計(jì)上,兩遍都會(huì)初始化一個(gè)list去存儲(chǔ)未初始化時(shí)候?qū)Ψ降姆椒ㄕ?qǐng)求产捞。
而對(duì)于每一個(gè)方法請(qǐng)求醇锚,通過(guò)key-value對(duì),去提供雙方解析坯临。
完善整體輪子
有了交互的接口焊唬,還有數(shù)據(jù)格式,雙方獲取到對(duì)方的方法調(diào)用進(jìn)行處理就不再困難看靠。
在其中赶促,唯一還有些繞的是雙方的回調(diào)。
js調(diào)用native的回調(diào)挟炬,可以在native中直接通過(guò)調(diào)用js的方式鸥滨,進(jìn)行回調(diào)函數(shù)的調(diào)用。
而native調(diào)用js的回調(diào)谤祖,還是要通過(guò)native截取js的方式婿滓,進(jìn)行回調(diào)函數(shù)的調(diào)用。
所以雙方都需要把本地的回調(diào)函數(shù)通過(guò)key-value對(duì)(Dictionary)存儲(chǔ)下來(lái)泊脐。
那在整體設(shè)計(jì)上空幻,雙方的方法解析與互相調(diào)用也應(yīng)該分離開(kāi)來(lái)烁峭,由此可以達(dá)到代碼模塊化的目的容客。
小結(jié):在完善輪子的時(shí)候,根據(jù)設(shè)計(jì)模式的原則约郁,進(jìn)行相應(yīng)的模塊化缩挑,方便代碼復(fù)用。
解析WebViewJavascriptBridge的源代碼
上一章我們整體設(shè)計(jì)了我們自己的一個(gè)native與js交互的輪子鬓梅,WebViewJavascriptBridge本身的做法也是類似供置,現(xiàn)在我們解析WebViewJavascriptBridge的源代碼來(lái)了解它是如何做到這每一步。
我們將js調(diào)用native的整個(gè)流程走一遍绽快,就可以完全清楚WebViewJavascriptBridge的邏輯芥丧。
首先是在頁(yè)面加載完成的時(shí)候紧阔,native會(huì)注入一段js代碼:
- (void)webViewDidFinishLoad:(UIView<FLWebViewProvider> *)webView {
if (webView != _webView) { return; }
_numRequestsLoading--;
if (_numRequestsLoading == 0 && ![[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:[_base webViewJavascriptCheckCommand]] isEqualToString:@"true"]) {
[_base injectJavascriptFile:YES webView:webView]; }
[_base dispatchStartUpMessageQueue];
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongDelegate webViewDidFinishLoad:(UIWebView *)webView];
}
}
其中
[_base injectJavascriptFile:YES webView:webView]
- (void)injectJavascriptFile:(BOOL)shouldInject webView:(UIView<FLWebViewProvider> *)webview {
if(shouldInject){
NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"WebViewJavascriptBridge.js" ofType:@"txt"];
NSString *jsBridge = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[self webview:webview evaluateJavaScript:jsBridge completionHandler:^(id callback, NSError *error) {
[self injectForDispatch:webview];
}];
}
}
將本地端的WebViewJavascriptBridge.js.txt注入到了web頁(yè)面的js代碼中
WebViewJavascriptBridge初始化時(shí)候,本身提供了ExampleApp.html頁(yè)面
我們從項(xiàng)目中找處一句js調(diào)用native的語(yǔ)句:
bridge.callHandler('testObjcCallback', {'foo': 'cccccccccccc'}, function(response) {
log('JS got response', response)
})
我們找到WebViewJavascriptBridge.js.txt
function callHandler(handlerName, data, responseCallback) {
_doSend({ handlerName:handlerName, data:data }, responseCallback)
}
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime()
responseCallbacks[callbackId] = responseCallback
message['callbackId'] = callbackId
}
sendMessageQueue.push(message)
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
}
其中续担,正如我們自己設(shè)計(jì)的擅耽,
首先,我們會(huì)將請(qǐng)求分為3個(gè)部分
- handlerName 方法名
- data 方法參數(shù)
- callback 回調(diào)
用dictionary的方式存儲(chǔ)他們物遇,組成了message乖仇,message本身就是我們發(fā)起的請(qǐng)求
而responseCallbacks通過(guò)特殊的callbackid,存儲(chǔ)下了這一次的函數(shù)回調(diào)
message增加了一個(gè)’callbackid’的參數(shù)询兴,去存儲(chǔ)這個(gè)callbackid
上面都是我們?cè)O(shè)計(jì)的方式乃沙,就是比較特殊的地方是,
WebViewJavascriptBridge將整個(gè)請(qǐng)求message诗舰,塞入了sendMessageQueue中警儒,而并非我們想當(dāng)然的塞入url重定向中。
然后使用messagingIframe發(fā)起重定向眶根。
function _createQueueReadyIframe(doc) {
messagingIframe = doc.createElement('iframe')
messagingIframe.style.display = 'none'
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
doc.documentElement.appendChild(messagingIframe)
}
var CUSTOM_PROTOCOL_SCHEME = 'wvjbscheme'
var QUEUE_HAS_MESSAGE = '__WVJB_QUEUE_MESSAGE__'
而這個(gè)重定向url由CUSTOM_PROTOCOL_SCHEME和QUEUE_HAS_MESSAGE組成冷蚂。
從這里可以看出,js調(diào)用本地代碼汛闸,并沒(méi)有直接將參數(shù)請(qǐng)求塞入重定向url蝙茶,而是塞入了一個(gè)list中,而所有的參數(shù)請(qǐng)求都是發(fā)了同一個(gè)诸老。
再看本地代碼隆夯,之前所說(shuō)的解析代碼:
本地 WebViewJavascriptBridge.m
- (BOOL)webView:(UIView<FLWebViewProvider> *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (webView != _webView) { return YES; }
NSURL *url = [request URL];
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if ([_base isCorrectProcotocolScheme:url]) {
if ([_base isCorrectHost:url]) {
NSString *messageQueueString = [(UIWebView*)webView stringByEvaluatingJavaScriptFromString:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
} else {
[_base logUnkownMessage:url];
}
return NO;
} else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return [strongDelegate webView:(UIWebView *)webView shouldStartLoadWithRequest:request navigationType:navigationType];
} else {
return YES;
}
}
其中_base是一個(gè)WebViewJavascriptBridgeBase對(duì)象,它包含了:
-(BOOL)isCorrectProcotocolScheme:(NSURL*)url {
if([[url scheme] isEqualToString:kCustomProtocolScheme]){
return YES;
} else {
return NO;
}
}
-(BOOL)isCorrectHost:(NSURL*)url {
if([[url host] isEqualToString:kQueueHasMessage]){
return YES;
} else {
return NO;
}
}
#define kCustomProtocolScheme @"wvjbscheme"
#define kQueueHasMessage @"__WVJB_QUEUE_MESSAGE__"
其中,很顯然别伏,通過(guò)檢查scheme和host蹄衷,就可以清楚的知道,這個(gè)請(qǐng)求是不是WebViewJavascriptBridge的重定向請(qǐng)求厘肮。
而
NSString *messageQueueString = [(UIWebView*)webView stringByEvaluatingJavaScriptFromString:[_base webViewJavascriptFetchQueyCommand]];
很明顯是在獲得之前sendMessageQueue
查找WebViewJavascriptBridgeBase.m和WebViewJavascriptBridge.js.txt文件
-(NSString *)webViewJavascriptFetchQueyCommand {
return @"WebViewJavascriptBridge._fetchQueue();";
}
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue)
sendMessageQueue = []
return messageQueueString
}
那這邊我們就了解了js調(diào)用本地端的方法:
js發(fā)起一個(gè)特殊的url請(qǐng)求愧口,告訴本地端,我發(fā)起請(qǐng)求了类茂。并且存儲(chǔ)相應(yīng)的回調(diào)函數(shù)和消息隊(duì)列耍属。
而本地端接收到消息,會(huì)去主動(dòng)拉取js中存儲(chǔ)消息的隊(duì)列巩检。
之后就是處理消息的方式:
[_base flushMessageQueue:messageQueueString];
找到WebViewJavascriptBridgeBase.m
- (void)flushMessageQueue:(NSString *)messageQueueString{
id messages;
if (messageQueueString) {
messages = [self _deserializeMessageJSON:messageQueueString];
}
if (![messages isKindOfClass:[NSArray class]]) {
NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [messages class], messages);
return;
}
for (WVJBMessage* message in messages) {
if (![message isKindOfClass:[WVJBMessage class]]) {
NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
continue;
}
[self _log:@"RCVD" json:message];
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
} else {
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
WVJBHandler handler;
if (message[@"handlerName"]) {
handler = self.messageHandlers[message[@"handlerName"]];
} else {
handler = self.messageHandler;
}
if (!handler) {
// [NSException raise:@"WVJBNoHandlerException" format:@"No handler for message from JS: %@", message];
cootek_log(@"No handler for message from JS: %@",message);
} else {
handler(message[@"data"], responseCallback);
}
}
}
}
首先厚骗,解析messages
messages = [self _deserializeMessageJSON:messageQueueString];
- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON {
return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
}
將他們轉(zhuǎn)回相應(yīng)的存儲(chǔ)Dictionary的list(NSArray)
然后遍歷每一個(gè)消息:
for (WVJBMessage* message in messages)
其中先判斷了:
NSString* responseId = message[@"responseId"];
我們之前發(fā)起的請(qǐng)求的dictionary,只包含 handlerName兢哭,data领舰, 和可能有的callbackId
所以我們一定是走else語(yǔ)句
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
而這部分代碼的邏輯,就是如果存在callbackid(表明存在callback),就去設(shè)置一個(gè)block responseCallback冲秽,提供回調(diào)
WVJBHandler handler;
if (message[@"handlerName"]) {
handler = self.messageHandlers[message[@"handlerName"]];
} else {
handler = self.messageHandler;
}
if (!handler) {
// [NSException raise:@"WVJBNoHandlerException" format:@"No handler for message from JS: %@", message];
cootek_log(@"No handler for message from JS: %@",message);
} else {
handler(message[@"data"], responseCallback);
}
這部分就是實(shí)際的函數(shù)調(diào)用舍咖,將相應(yīng)的handler取出,并且進(jìn)行調(diào)用
handler(message[@"data"], responseCallback);
由其可見(jiàn)锉桑,我們本地端必須先要在self.messageHandlers中谎仲,包含這樣的一個(gè)消息,才有可能進(jìn)行執(zhí)行刨仑,所以郑诺,本地端必須先注冊(cè)相應(yīng)的方法。
即:
[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);
responseCallback(@"Response from testObjcCallback");
}];
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
首先要在bridge中注冊(cè)了testObjcCallback方法杉武,才會(huì)去執(zhí)行到這段代碼辙诞。
即,一開(kāi)始轻抱,在本地端飞涂,你必須先registerHandler,將相應(yīng)的block隊(duì)贏的handlername注冊(cè)到_base.messageHandlers中祈搜,表示存在這樣的方法较店,
然后當(dāng)你在js中callHandler的時(shí)候,就會(huì)通過(guò)一系列調(diào)用容燕,找到這個(gè)handler方法梁呈,并且最后執(zhí)行它。
而在執(zhí)行block的函數(shù)的時(shí)候蘸秘,也會(huì)包含
responseCallback(@"Response from testObjcCallback");
進(jìn)行回調(diào)的執(zhí)行
回到回調(diào)的定義:
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
如果回調(diào)存在官卡,就會(huì)組成這樣一條msg
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
包含了兩個(gè)key, responseid與responseData
[self _queueMessage:msg];
- (void)_queueMessage:(WVJBMessage*)message {
if (self.startupMessageQueue) {
[self.startupMessageQueue addObject:message];
} else {
[self _dispatchMessage:message];
}
}
走到[self _dispatchMessage:message]
函數(shù)
(self.startupMessageQueue的目的是存儲(chǔ)沒(méi)有初始化時(shí)候的方法,提供之后調(diào)用)
- (void)_dispatchMessage:(WVJBMessage*)message {
NSString *messageJSON = [self _serializeMessage:message];
[self _log:@"SEND" json:messageJSON];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
if ([[NSThread currentThread] isMainThread]) {
[_flWebView evaluateJavaScript:javascriptCommand completionHandler:nil];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[_flWebView evaluateJavaScript:javascriptCommand completionHandler:nil];
});
}
}
調(diào)用到j(luò)s的_handleMessageFromObjC方法
function _handleMessageFromObjC(messageJSON) {
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON)
} else {
_dispatchMessageFromObjC(messageJSON)
}
}
走到_dispatchMessageFromObjC(messageJSON)函數(shù)
(receiveMessageQueue的目的是存儲(chǔ)沒(méi)有初始化時(shí)候的方法,提供之后調(diào)用)
function _dispatchMessageFromObjC(messageJSON) {
setTimeout(function _timeoutDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON)
var messageHandler
if (message.responseId) {
var responseCallback = responseCallbacks[message.responseId]
if (!responseCallback) { return; }
responseCallback(message.responseData)
delete responseCallbacks[message.responseId]
} else {
var responseCallback
if (message.callbackId) {
var callbackResponseId = message.callbackId
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}
}
var handler = WebViewJavascriptBridge._messageHandler
if (message.handlerName) {
handler = messageHandlers[message.handlerName]
}
try {
handler(message.data, responseCallback)
} catch(exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)
}
}
}
})
}
這里的代碼是不是很熟悉?幾乎和flushMessageQueue的后半邏輯代碼是一樣的
也是先檢查responseid
if (message.responseId)
我們請(qǐng)求的參數(shù):
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
正包含了reponseid
var responseCallback = responseCallbacks[message.responseId]
if (!responseCallback) { return; }
responseCallback(message.responseData)
delete responseCallbacks[message.responseId]
以上代碼就是從responseCallbacks取出相應(yīng)的callback偶宫,然后執(zhí)行完刪除。
這樣孝冒,整個(gè)js調(diào)用native代碼就完成了。
同理可得native調(diào)用js的方式
js中注冊(cè)handler
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler
}
native調(diào)用callhandler
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
NSMutableDictionary* message = [NSMutableDictionary dictionary];
if (data) {
message[@"data"] = data;
}
if (responseCallback) {
NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
self.responseCallbacks[callbackId] = [responseCallback copy];
message[@"callbackId"] = callbackId;
}
if (handlerName) {
message[@"handlerName"] = handlerName;
}
[self _queueMessage:message];
}
同樣是包含了self.responseCallbacks本地的回調(diào)函數(shù)隊(duì)列,和message消息
然后執(zhí)行
[self _queueMessage:message];
到j(luò)s的_dispatchMessageFromObjC
函數(shù)
此時(shí)也是沒(méi)有reponseid
所以會(huì)去創(chuàng)建回調(diào)函數(shù),并且執(zhí)行js中對(duì)應(yīng)的handler
var responseCallback
if (message.callbackId) {
var callbackResponseId = message.callbackId
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}
}
var handler = WebViewJavascriptBridge._messageHandler
if (message.handlerName) {
handler = messageHandlers[message.handlerName]
}
try {
handler(message.data, responseCallback)
} catch(exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)
}
}
當(dāng)js中調(diào)用回調(diào)函數(shù)的時(shí)候
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}
通過(guò)_doSend
方法
也包含了reponseid
與responsedata
到本地的flushMessageQueue
中
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
}
objectivec
執(zhí)行相應(yīng)的回調(diào)函數(shù)叫挟。
總結(jié)
WebViewJavascriptBridge通過(guò)兩種數(shù)據(jù)結(jié)構(gòu)
請(qǐng)求數(shù)據(jù) handlerName,data,callbackid 回調(diào)數(shù)據(jù) responseId , responseData
js通過(guò)url重定向,讓本地端主動(dòng)拉取js的請(qǐng)求數(shù)據(jù)進(jìn)行函數(shù)調(diào)用柑肴,然后native再主動(dòng)調(diào)用js代碼霞揉,調(diào)用回調(diào)函數(shù)
native通過(guò)主動(dòng)調(diào)用js的代碼去進(jìn)行函數(shù)調(diào)用旬薯,然后js再通過(guò)url重定向晰骑,調(diào)用回調(diào)函數(shù)
關(guān)于UIWebView的內(nèi)存泄漏問(wèn)題,這邊有個(gè)blog,講解的比較好硕舆,可以緩解一下秽荞。
參考鏈接
1.WebViewJavascriptBridge 原理分析
2.WebViewJavascriptBridge-Obj-C和JavaScript互通消息的橋梁
3.UIWebView Secrets - Part1 - Memory Leaks on Xmlhttprequest