WebViewJavascriptBridge 原理解析
栏目: JavaScript · 发布时间: 6年前
内容简介:WebViewJavascriptBridge是项目中常用的OC与js交互的第三方框架,它没有通过苹果的JavascriptCore框架来实现,而是实现了自己的逻辑。下面看一下Message的数据结构,当然在传送的过程中是一JSON的格式进行传送的,以OC为例:
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!
以上所述就是小编给大家介绍的《WebViewJavascriptBridge 原理解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。