WebViewJavascriptBridge 分析oc向js发送消息处理流程
2015-08-14 11:40
369 查看
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,这样就完成了交互。
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,这样就完成了交互。
相关文章推荐
- Android客户端和服务端如何使用Token和Session
- 微信提示
- android抽屉实现
- Android中SMS的接收处理
- iOS开发总结——项目目录结构
- cocos2d-x-3.4 无法打开包含文件extensions/ExtensionExport.h
- AutoMapper 最佳实践
- android 安装目录介绍
- 02 AppCan入门学习之弹性盒子模型
- php、java、android、ios通用的3des加密方法
- Android Studio导入JAR包(以ButterKnife为例)
- Android fragment 只让一个fragment支持横屏
- ubuntu 14.04 编译android4.0 出现gcc-version.sh: line 11: cc: command not found错误解决方法
- Android 学习之逐帧动画(Frame)
- Android 导入v7包常见错误,以及项目引用v7包错误解决,
- 史上最简单的android图片自动轮播
- Android之WebView后退按钮为返回上一个页面
- Android签名打包后应用HOME键重启
- Unity Shader 学习笔记 (一) 创建Shader自带代码解读
- 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二节:Cocos引擎开发环境搭建与项目创建!