您的位置:首页 > 移动开发

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,这样就完成了交互。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: