1.优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案
2. WebViewJavascriptBridge 原理分析
过程分析如下:
- (void)sendMessage:(id)sender {
// responseCallback供js返回数据后回调
[_bridge send:@"A string sent from ObjC to JS" responseCallback:^(id response) {
NSLog(@"sendMessage got response: %@", response);
}];
}
- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[self _sendData:data responseCallback:responseCallback handlerName:nil];
}
- (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];
//用callbackId 标示responseCallback并保存起来,供js返回数据后回调
_responseCallbacks[callbackId] = [responseCallback copy];
message[@"callbackId"] = callbackId;
}
if (handlerName) {
message[@"handlerName"] = handlerName;
}
[self _queueMessage:message];
}
- (void)_queueMessage:(WVJBMessage*)message {
if (_startupMessageQueue) {
[_startupMessageQueue addObject:message];
} else {
[self _dispatchMessage:message];
}
}
- (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"];
//调用javascript中的函数_handleMessageFromObjC
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC(‘%@‘);", messageJSON];
if ([[NSThread currentThread] isMainThread]) {
[_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
} else {
__strong WVJB_WEBVIEW_TYPE* strongWebView = _webView;
dispatch_sync(dispatch_get_main_queue(), ^{
[strongWebView stringByEvaluatingJavaScriptFromString:javascriptCommand];
});
}
}
//处理来自ObjC的消息【native端调用】
function _handleMessageFromObjC(messageJSON) {
//如果接收队列对象存在则入队该消息,否则直接处理
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON)
} else {
_dispatchMessageFromObjC(messageJSON)
}
}
//内部方法:处理来自objc的消息
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
//供handle处理完后回调
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}
}
//走这里,此处handler等于WebViewJavascriptBridge.init时的_messageHandler
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)
}
}
}
})
}
//初始化操作,并定义默认的消息处理逻辑
bridge.init(function(message, responseCallback) {
log(‘JS got a message‘, message)
var data = { ‘Js Responds‘:‘Web!‘ }
log(‘JS responding with‘, data)
responseCallback(data)
})
//初始化方法,注入默认的消息处理器 ,默认的消息处理器用于在处理来自objc的消息时,如果该消息没有设置处理器,则采用默认处理器处理
function init(messageHandler) {
if (WebViewJavascriptBridge._messageHandler) { throw new Error(‘WebViewJavascriptBridge.init called twice‘) }
WebViewJavascriptBridge._messageHandler = messageHandler
var receivedMessages = receiveMessageQueue
receiveMessageQueue = null
for (var i=0; i<receivedMessages.length; i++) {
_dispatchMessageFromObjC(receivedMessages[i])
}
}
//内部方法:消息的发送
function _doSend(message, responseCallback) {
//如果定义了回调
if (responseCallback) {
//为回调对象产生唯一标识
var callbackId = ‘cb_‘+(uniqueId++)+‘_‘+new Date().getTime()
//并存储到一个集合对象里
responseCallbacks[callbackId] = responseCallback
//新增一个key-value对- ‘callbackId‘:callbackId
message[‘callbackId‘] = callbackId
}
//把发送的消息至于队列中,然后下面的函数通知oc来取message
sendMessageQueue.push(message)
//产生一个src(url scheme),供native中shouldStartLoadWithRequest捕捉
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + ‘://‘ + QUEUE_HAS_MESSAGE
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (webView != _webView) { return YES; }
NSURL *url = [request URL];
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if ([[url scheme] isEqualToString:kCustomProtocolScheme]) {
if ([[url host] isEqualToString:kQueueHasMessage]) {
[self _flushMessageQueue];//取出js放进去的消息,并处理
} else {
NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", kCustomProtocolScheme, [url path]);
}
return NO;
} else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
} else {
return YES;
}
}
- (void)_flushMessageQueue {
//直接调用js的函数取出消息
NSString *messageQueueString = [_webView stringByEvaluatingJavaScriptFromString:@"WebViewJavascriptBridge._fetchQueue();"];
id 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"]);
[_responseCallbacks removeObjectForKey:responseId];//删除回调函数
} else {
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
responseCallback = ^(id responseData) {
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
WVJBHandler handler;
if (message[@"handlerName"]) {
handler = _messageHandlers[message[@"handlerName"]];
if (!handler) {
NSLog(@"WVJB Warning: No handler for %@", message[@"handlerName"]);
return responseCallback(@{});
}
} else {
handler = _messageHandler;
}
@try {
id data = message[@"data"];
handler(data, responseCallback);
}
@catch (NSException *exception) {
NSLog(@"WebViewJavascriptBridge: WARNING: objc handler threw. %@ %@", message, exception);
}
}
}
}
//获得队列,将队列中的每个元素用分隔符分隔之后连成一个字符串【native端调用】
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue)
sendMessageQueue = []
return messageQueueString
}
总结oc把oc的data和回调函数的ID发给js,js返回js的data和回调函数的ID,这样就完成了交互。
版权声明:本文为博主原创文章,未经博主允许不得转载。
WebViewJavascriptBridge 分析oc向js发送消息处理流程
原文:http://blog.csdn.net/seuliujiaguo/article/details/47659405