WebViewJavascriptBridge是项目中常用的OC与js交互的第三方框架,它没有通过苹果的JavascriptCore框架来实现,而是实现了自己的逻辑。
WebViewJavascriptBridgeBase
是OC模块的功能实现类,对应的js功能类为WebViewJavascriptBridge_JS
,此处巧妙地使用字符串来解决了之前版本集成打包需要bundle加载js模块的问题。
先说明几个数据结构:
@property (strong, nonatomic) NSMutableDictionary messageHandlers;
,以字典的形式保存注册的Hander,js相似数据结构为:var messageHandlers = {};
NSMutableDictionary* responseCallbacks;
,以字典的形式保存调用Handler后回调给js消息的Block,js相似数据结构为:var responseCallbacks = {};
下面看一下Message的数据结构,当然在传送的过程中是一JSON的格式进行传送的,以OC为例:
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;
}
data:要传递的数据(如参数)。
callbackId: 唯一标示作为responseCallBack的Key
handerName:Handler唯一标示(Key)
对应js代码:
_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;
}
发送消息,OC为调用WebView调用evaluateJavascript
方法来调用js,通过WebViewJavascriptBridge来调用js的_handleMessageFromObjC方法
主要代码:
- (void)_dispatchMessage:(WVJBMessage*)message {
NSString *messageJSON = [self _serializeMessage:message pretty:NO];
[self _log:@"SEND" json:messageJSON];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
<-------------省略-------------->
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
if ([[NSThread currentThread] isMainThread]) {
[self _evaluateJavascript:javascriptCommand];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[self _evaluateJavascript:javascriptCommand];
});
}
}
而js调用OC则是通过WebView加载Request来实现:
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;
}
- 添加Message到待处理消息集合MessageQueue,然后调用
messagingIframe.src
来加载Request,格式为:https://__wvjb_queue_message__
- (BOOL)webView:(UIWebView *)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 isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
} else {
[_base logUnkownMessage:url];
}
return NO;
} else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
} else {
return YES;
}
}
2.获取要处理的js消息,进行处理,先说一下代码里的判断函数
isWebViewJavascriptBridgeURL
:判断是否是Bridge处理的URL格式,为了兼容旧版本,判断了2个Scheme,然后继续下面2个函数的判断。
isBridgeLoadedURL
:判断是否是Bridge初始化加载URL格式,对应格式为:https://__bridge_loaded__
,如果是,则调用injectJavascriptFile函数,加载初始化的js文件(WebViewJavascriptBridge_JS)
isQueueMessageURL
:判断是否是Bridge发送消息的URL格式,对应格式为:https://__wvjb_queue_message__
当url是https://__wvjb_queue_message__
,则表示js有消息要处理,WebView加载Request,调用OC[self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]
这个方法来调用js文件中的
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
方法,来获取要处理的消息,然后调用OC的flushMessageQueue:
方法来处理消息
处理消息,OC处理js方法为- (void)flushMessageQueue:(NSString *)messageQueueString{xxx}
先看这部分代码:
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 = self.messageHandlers[message[@"handlerName"]];
if (!handler) {
NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
continue;
}
handler(message[@"data"], responseCallback);
如果存在callbackId,则代表js存在responseCallBack,需要OC回调,所以需要发送回调消息即`WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; [self _queueMessage:msg];只是callbackId的KEY设置成responseId来区分是回调。 再看代码:
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
}
首先判断是否有responseId(即OC的ResponseCallBack保存时候的CallbackId),如果有,那么代表这个消息是js的回调消息(OC调用js注册的Handler),则直接处理。
如果我的文章对你有所帮助,请留言告诉我,Thanks!