上一篇写了WebViewJavascriptBridge的OC调用JS的部分,这篇主要看看JS是怎么调用OC方法的。
一、OC注册方法等待JS调用
//注册handler等待js调用
[_bridge registerHandler:@"OC-fun1" handler:^(id data, WVJBResponseCallback responseCallback) {
//NSLog(@"testObjcCallback called: %@", data);
//不调用这个方法,js中调用OC方法传递的函数不会被调用,“JS调用OC的返回值”不会被打印。
responseCallback(@"OC发给JS的返回值");
}];
//WKWebViewJavascriptBridge.m
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
只是把handler放到WebViewJavascriptBridgeBase对象的字典messageHandlers里。
二、JS中是如何调用OC方法的
1.先看页面中按钮的点击处理方法
document.getElementById('buttons').onclick = function(e) {
e.preventDefault()
var params = {'JS调用OC参数': '参数值'};
log('JS马上调用OC方法',params)
//因其在function callback(bridge)定义,所以bridge是存在的。第三个参数是个回调函数。
bridge.callHandler('OC-fun1',params, function(response) {
log('JS调用OC的返回值', response)
})
}
//WebViewJavascriptBridge_JS.m
function callHandler(handlerName, data, responseCallback) {
if (arguments.length == 2 && typeof data == 'function') {
responseCallback = data;
data = null;
}
_doSend({ handlerName:handlerName, data:data }, responseCallback);
}
//这个函数和上一篇中调用回调的方法是一样的。JS调用OC都是通过修改iframe的url实现。不同的是这里传递了responseCallback参数,这个其实就是要调用JS的回调函数(类似OC的block)。
function _doSend(message, responseCallback) {
if (responseCallback) {
//生成方法对应的id值,存到对象里key是callbackId,值是responseCallback回调函数。
//再把这个方法的id值,放到message里,供OC通过_fetchQueue这个js方法获取(通过evaluateJavaScript)。
var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message['callbackId'] = callbackId;
}
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
//这里又到了WKWebViewJavascriptBridge里面webView:decidePolicyForNavigationAction: decisionHandler方法。在里面会调用[self WKFlushMessageQueue],我们继续看代码
//WKWebViewJavascriptBridge.m
- (void)WKFlushMessageQueue {
[_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
if (error != nil) {
NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
}
[_base flushMessageQueue:result];
}];
}
//WebViewJavascriptBridgeBase.m
(void)flushMessageQueue:(NSString *)messageQueueString{
if (messageQueueString == nil || messageQueueString.length == 0) {
NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
return;
}
//JSON字符串反序列化为数组,数组里的元素是字典类型。
id messages = [self _deserializeMessageJSON:messageQueueString];
for (WVJBMessage* message in messages) {
if (![message isKindOfClass:[WVJBMessage class]]) {
NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
continue;
}
[self _log:@"RCVD" json:message];
//OC调用JS会传递responseId,JS调用OC传递的是callbackId。
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
//这里调用了OC调用JS方法时传入的block。
responseCallback(message[@"responseData"]);
//调用结束及时移除这个block
[self.responseCallbacks removeObjectForKey:responseId];
} else {
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
//responseCallback这个block,是用来调用JS调用OC时传入的函数的。
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
//通过message[@"handlerName"]这里面存到是OC的函数名,作为key在字典messageHandlers中,找到对应的block。
WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
if (!handler) {
NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
continue;
}
//调用了OC注册的方法。responseCallback在上面已经定义了,用来回调JS方法里的函数的。
handler(message[@"data"], responseCallback);
}
}
}
给JS传值,也就是调用JS方法里的那个函数。这通过responseCallback完成,这和上篇的代码很想。
- (void)_queueMessage:(WVJBMessage*)message {
if (self.startupMessageQueue) {
[self.startupMessageQueue addObject:message];
} else {
[self _dispatchMessage:message];
}
}
//把消息发送给WEB环境 yuxg
- (void)_dispatchMessage:(WVJBMessage*)message {
NSString *messageJSON = [self _serializeMessage:message pretty:NO];
[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]) {
[self _evaluateJavascript:javascriptCommand];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[self _evaluateJavascript:javascriptCommand];
});
}
}
//WebViewJavascriptBridge_JS.m
function _handleMessageFromObjC(messageJSON) {
_dispatchMessageFromObjC(messageJSON);
}
function _dispatchMessageFromObjC(messageJSON) {
if (dispatchMessagesWithTimeoutSafety) {
setTimeout(_doDispatchMessageFromObjC);
} else {
_doDispatchMessageFromObjC();
}
function _doDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON);
var messageHandler;
var responseCallback;
//alert("no:"+message.responseId);
if (message.responseId) {
//JS调用原生的方法走这个分支。
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
//这里调用了JS方法调用原生的方法是传入的函数。得到OC传给JS的返回值整个过程结束。
responseCallback(message.responseData);
//执行过后把对应的内容删除
delete responseCallbacks[message.responseId];
} else {
//alert(message.callbackId); //objc_cb_1 objc_cb_2 objc_cb_3....
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
};
}
var handler = messageHandlers[message.handlerName];
if (!handler) {
console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
} else {
handler(message.data, responseCallback);
}
}
}
}