当前移动端 H5 与原生混合开发已成常态,这其中自然就少不了原生与 H5 的交互问题。iOS 端 WebViewJavascriptBridge 作为一个比较流行的三方库,很好的解决了原生与 H5 交互的问题。使攻城狮们专注于业务代码的编写而不必在意其中交互的细节。但做为一名优秀的工程师不仅要知其然,还要知其所以然。下面我就来扒一扒 WebViewJavascriptBridge 背后的原理。
啰嗦两句:goole 或 baidu 一下,对 WebViewJavascriptBridge 原理的讲解能搜一大把,这里为什么还要再去开一篇文章呢?
- 是能搜索出来一大把,但感觉真正讲明白的却不多(不知道我能不能讲明白,我努力吧);
- 学习之后总结一下是个良好的习惯,真正能把东西学到手;
- 分享一下,希望跟大家交流交流;
鉴于此,在与小伙伴们一起学习的同时,还望大家指正不对的地方,共同学习进步。
进入正题...
WebViewJavascriptBridge
版本:V6.0.3
github 地址:https://github.com/marcuswestin/WebViewJavascriptBridge
温馨提示
为了方便阅读本文,最好去 github 上 clone 一份 demo 工程。
先来看一下 WebViewJavascriptBridge
的全家福。
1. WebViewJavascriptBridge_JS
JS 端用来收发消息的类,纯 JS 代码。其做为 html 的 bridge,负责(主动)对原生界面发送消息,整理并(被动的向原生界面)传输数据,并向 html 发送回调信息;
2. WebViewJavascriptBridge
- 桥接的入口,针对不同类型的 webView(UIWebView,WKWebView,WebView)进行分发;
- 针对 UIWebView 和 WebView 做的一层封装,主要用来执行 JS 代码,以及实现 UIWebView 和 WebView的代理方法,并通过拦截 URL 来通知 WebViewJavascriptBridgeBase 做相应操作
3. WKWebViewJavascriptBridge
针对 WKWebView 做的一层封装,主要用来执行 JS 代码,以及实现 WKWebView 的代理方法,并通过拦截 URL 来通知 WebViewJavascriptBridgeBase 做相应操作
4. WebViewJavascriptBridgeBase
- 用来进行 bridge 初始化和消息处理的核心类;
- 这个类是在支持 WKWebView 后从 WebViewJavascriptBridge 中独立出来的逻辑,专门用来处理 bridge 相关的逻辑,不再与具体的 Web View 相关联了
理一下它们的关系
WebViewJavascriptBridge_JS
可以看成是 html 文件中 JS 代码的 bridge,主要服务于 html 中的 JS 方法功能的;另外的三个 WebViewJavascriptBridge
, WKWebViewJavascriptBridge
, WebViewJavascriptBridgeBase
是 webView
的 bridge
,主要服务于原生的。这两个 bridge 之间主要通过 webView
的代理方法webView:decidePolicyForNavigationAction:decisionHandler:
和 stringByEvaluatingJavaScriptFromString:
方法进行消息的传递和数据的传输。
温馨提示
以下均以 WebViewJavascriptBridge
为例进行讲解。
WebViewJavascriptBridge_JS
有分别用于存储 JS 注册的 handler 和 responseCallback 回调的容器。
var messagingIframe;
var sendMessageQueue = []; // 用于存储 JS 传给原生的数据
var messageHandlers = {}; // 用于存储 JS 注册的 handler
var CUSTOM_PROTOCOL_SCHEME = 'https';
var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
// 用于存储 JS 注册的回调,主要用于回传数据给原生
var responseCallbacks = {};
var uniqueId = 1;
var dispatchMessagesWithTimeoutSafety = true;
WebViewJavascriptBridgeBase
的.h
文件中也有分别用于存储 OC 注册的 handler
和 responseCallback
容器
@property (strong, nonatomic) NSMutableArray* startupMessageQueue;
// 用于存储注册的回调,主要用于向 JS 回传数据
@property (strong, nonatomic) NSMutableDictionary* responseCallbacks;
// 用于存储注册的 handler
@property (strong, nonatomic) NSMutableDictionary* messageHandlers;
@property (strong, nonatomic) WVJBHandler messageHandler;
由于 OC 可以通过 stringByEvaluatingJavaScriptFromString:
方法直接向 JS 传数据,所以它没有用于存储向 JS 传输数据的容器。
由于 OC 和 JS 各自的 handler 和 callback 回调无法在对方的环境中直接进行调用,所以他们在各自的 bridge 中设置存储 handler 和 callback 的容器。然后仅通过传输的数据各自执行自己的 handler 方法和 callback 回调。
核心 API
OC
// 初始化 WebViewJavascriptBridge 对象
+ (instancetype)bridgeForWebView:(id)webView;
// 设置 WebViewJavascriptBridge 对象的代理
- (void)setWebViewDelegate:(id)webViewDelegate;
// OC 注册方法供 JS 调用
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler
// OC 调用注册的 JS 方法
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback
JS
// jS 注册方法供 OC 调用
function registerHandler(handlerName, handler)
// JS 调用注册的 OC 方法
function callHandler(handlerName, data, responseCallback)
如果想要 OC 调用 JS 的方法,那么 OC 需要执行
callHandler:
方法,在调用之前 html 的 bridge 实例需要执行 registerHandler
方法以向 WebViewJavascriptBridge_JS
注册被调用的 handler;
同理,JS 调用 OC 的方法,JS 需要要执行 callHandler
方法,在这之前 OC 的 bridge 需要执行 registerHandler:
方法以向 WebViewJavascriptBridgeBase
注册 handler。
通过 WebViewJavascriptBridge
的 demo 你会发现,html 和 OC 中的方法注册和调用是一一对应的,两边必然会有一个相同的 handlerName
。
JS 的方法注册和调用在 WebViewJavascriptBridge_JS
中进行,WebViewJavascriptBridge_JS
负责管理 JS 的消息发送和接收;OC 的方法注册和调用则在 WebViewJavascriptBridgeBase
中进行,WebViewJavascriptBridgeBase
负责 OC 的消息发送和接收。
原生与 H5 交互流程
以下流程以 WebViewJavascriptBridge
中的 demo 为例,请参考 demo 中示例代码进行阅读。
OC 调用 JS
1. OC 调用 callhandler:
方法
[_bridge callHandler:@"testJavascriptHandler"
data:@{ @"foo":@"before ready" }
responseCallback:^(id responseData) {
NSLog(@"JS 回调数据:%@", responseData);
}];
handlerName
为 testJavascriptHandler
,传过去的数据为一个字典 @{ @"foo":@"before ready" }
,还有一个接收 JS 的回调 responseCallback
2. 处理 handlerName
,传过去的 data
数据以及回调
跳转到 callHandler:data:responseCallback:
实现里面去,会走到 WebViewJavascriptBridgeBase
中的如下方法
- (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];
self.responseCallbacks[callbackId] = [responseCallback copy];
message[@"callbackId"] = callbackId;
}
if (handlerName) {
message[@"handlerName"] = handlerName;
}
[self _queueMessage:message];
}
这个方法对 handlerName
, data
进行了封装处理,将它们放到了字典 message 里面。而 responseCallback
由于是 OC 的闭包,无法在 JS 中进行直接调用,这里生成了一个 callbackId,并以 callbackId
为 key
将其存储在了 WebViewJavascriptBridgeBase
对象的 responseCallbacks
容器里。同时将生成的 callbackId 以字符串 callbackId 为 key
,封装到了字典 message 里面。接着将 message 作为参数传到 _queueMessage:
函数里面进行执行。
3. 调用 webView
的 stringByEvaluatingJavaScriptFromString:
方法执行 JS 代码
接上面 _queueMessage
方法,我们跳转到 _dispatchMessage:
方法
- (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];
});
}
}
这里将 message 数据转为了 JSON 字符串之后与 @"WebViewJavascriptBridge._handleMessageFromObjC('%@');"
进行了拼接,得到 javascriptCommand
字符串。最终会在 WebViewJavascriptBridge
的如下方法里面执行这段 JS 代码
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
}
4. 在 WebViewJavascriptBridge_JS
执行 _handleMessageFromObjC
方法
上个步骤中最后执行 JS 代码会执行 WebViewJavascriptBridge_JS
中的 _handleMessageFromObjC
方法
function _handleMessageFromObjC(messageJSON) {
_dispatchMessageFromObjC(messageJSON);
}
来看看 _dispatchMessageFromObjC
方法
function _dispatchMessageFromObjC(messageJSON) {
if (dispatchMessagesWithTimeoutSafety) {
setTimeout(_doDispatchMessageFromObjC);
} else {
_doDispatchMessageFromObjC();
}
function _doDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON);
var messageHandler;
var responseCallback;
if (message.responseId) {
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
responseCallback(message.responseData);
delete responseCallbacks[message.responseId];
} else {
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);
}
}
}
}
有点长!
这里我们主要看 _doDispatchMessageFromObjC
中 else
分支里面的代码。
通过上一步 OC 传过来的参数 messageJSON,在这里被转成了 message 对象。message 里面目前的数据应该是这样的
{"callbackId": callbackId, "data": {"foo": "before ready"}, "handlerName": "testJavascriptHandler"}
这里先取出了 message
中的 callbackId,并初始化了一个 responseCallback
回调,然后根据 message
里面的 handlerName
取出注册在 WebViewJavascriptBridge_JS 中 messageHandlers 容器内的 handler 方法进行调用。在执行 handler(message.data, responseCallback)
时会触发 ExampleApp.html 文件中注册方法的回调
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
此时上面方法中有 data 数据即为 {"foo": "before ready"},OC 传递数据给 H5 完毕。
接着执行的
responseCallback(responseData)
会将 { 'Javascript Says':'Right back atcha!' } 传出去。
回到 _doDispatchMessageFromObjC
中 else
分支代码里面去,此时会执行下面这段代码,其中 responseData
即为 { 'Javascript Says':'Right back atcha!' }。
注意_doSend
方法中的 responseId:callbackResponseId 的 key 是 responseId,而不再是 callbackId 了,不过值还是原生传过来的 callbackId 值没变,这对后续通过这个 Id 值查找对应的回调是至关重要的
responseCallback = function(responseData) {
_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
};
5. 在 _doSend
方法里面向通过代理向原生发送回调消息
来看 _doSend
方法。
我们向传了这么一个 JS 字典对象 { handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData } 。
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;
}
此时,responseCallback 是 undefined 的,所以 if
判断里面的代码不会执行。会直接执行最后面的两句代码。
sendMessageQueue.push(message)
会将 message 数据放到一个数组里面。这里很重要,里面的值后面将会被取走。
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
则会触发 WebViewJavascriptBridge
中
webView 的如下代理方法
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener {
if (webView != _webView) { return; }
NSURL *url = [request URL];
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];
}
[listener ignore];
} else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
} else {
[listener use];
}
}
进入到此方法后会走到 else if [_base isQueueMessageURL:url]
的判断分支代码里面。我们就来详细的这个分支里面的代码
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
分别查看一下 [_base webViewJavascriptFetchQueyCommand]
和 _evaluateJavascript:
方法的实现,你会发现这就是一个从 WebViewJavascriptBridge_JS
取数据的过程。上面我们存储在 sendMessageQueue
中的数据将会被取出。之前存进的数据是这样的:{ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }
然后这个取出的数据会作为参数传入 flushMessageQueue:
方法
- (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;
}
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];
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
} else {
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);
}
}
}
又是一个比较长的方法!不过我们主要关心 if (responseId)
分支里面的代码就可以了。
这里会根据message 中的 responseId 从 responseCallbacks
容器中取出对应的 callback 回调,然后再将 message 中的 data 作为参数传进回调,从而将 JS 中的数据成功传给 OC 。
至此,OC 调用 JS 方法的流程算是走完了。是不是太绕了!!!
中场休息五分钟,再接着看!
JS 调用 OC
1. JS 调用 callHandler 方法
以 ExampleApp.html 中的 callhandler 方法为例进行讲解
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
})
这里我们要调用 OC 中的 handlerName
为 testObjcCallback 的 handler,要传过去的数据为 {"foo": ''bar"},后面是的 function(response) { }
则是接收原生的回调
2. 来看 WebViewJavascriptBridge_JS
中的 callHandler
方法
function callHandler(handlerName, data, responseCallback) {
if (arguments.length == 2 && typeof data == 'function') {
responseCallback = data;
data = null;
}
_doSend({ handlerName:handlerName, data:data }, responseCallback);
}
以上方法 if
分支中的代码不会执行
直接来到 _doSend
方法
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;
}
这里,由于 JS 的 responseCallback 是不能在 OC 中进行使用的,因此也是生成 callbackId 进行传递。同时将 responseCallback 存储到 responseCallbacks
容器里面,以备后续进行回调。
这里 sendMessageQueue
里面存入了封装好的 message 对象,同理也是是需要后续被取出使用的。
3. iframe.src 触发 webview 代理方法
上一步最后一句代码会触发 WebViewJavascriptBridge
中 webview 的代理方法
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener {
if (webView != _webView) { return; }
NSURL *url = [request URL];
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];
}
[listener ignore];
} else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
} else {
[listener use];
}
}
这里我们依然只关注 else if ([_base isQueueMessageURL:url])
分支中的代码。
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
第一句代码依然是取 WebViewJavascriptBridge_JS
中 sendMessageQueue 中的数据,数据是这样的 { handlerName:handlerName, data:data, callbackId:callbackId }
然后拼接成一个 JSON 字符串供 flushMessageQueue: 函数进行调用。
- (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;
}
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];
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
} else {
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);
}
}
}
这里,我们主要关心的是 else
分支里面的代码。
取出 message 中的 callbackId 同时初始化 responseCallback 回调。
然后通过 message 中的 hanlerName 取出 messageHandlers 容器中对应的 handler 进行调用
handler(message[@"data"], responseCallback)
以 message 中的 data 和 responseCallback 为参数,这里 handler 的执行会引发以下注册方法中回调的执行
[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);
responseCallback(@"Response from testObjcCallback");
}];
此时,JS 中的 {'foo': 'bar'} 数据就成功的传入了原生回调中。同时,上面方法中的 responseCallback 回调会将 OC 中@"Response from testObjcCallback"
带出来,配合 message 中的 callbackId 一同进行回调的执行执行如下回调
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
注意这里的 key 也变成了 responseId,而 callbackId 值还是 JS 传过来的值。
**4. responseCallback 回传数据给 H5 **
走到 _queueMessage: 方法,然后走到 _dispatchMessage:
方法,最后是 _evaluateJavascript:
方法,最终又回到 WebViewJavascriptBridge_JS
的_handleMessageFromObjC
方法。此时 messageJSON 就上而传进来的 msg 。
function _dispatchMessageFromObjC(messageJSON) {
if (dispatchMessagesWithTimeoutSafety) {
setTimeout(_doDispatchMessageFromObjC);
} else {
_doDispatchMessageFromObjC();
}
function _doDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON);
var messageHandler;
var responseCallback;
if (message.responseId) {
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
responseCallback(message.responseData);
delete responseCallbacks[message.responseId];
} else {
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);
}
}
}
}
只看 if (message.responseId)
分支里面的代码。
分支中代码会从 responseCallbacks
容器中根据 responseId 取出对应的 callback 进行回调,参数为从 OC 传过来的 responseData 数据,从而成功将 OC 的数据回调给 ExampleApp.html 中的 callhander 方法的 responseCallback 回调中去
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
})
至此,JS 调用 OC 方法完成!
终于梳理完了。可以舒口气了!
如果你感觉哪里有不正确或不理解的地方,欢迎下方留言交流~
部分内容参考自
https://www.jianshu.com/p/6f34903be630