上一篇讲到来自JS的请求是如何调用原生代码的,这一篇来看原生的代码是如何调用JS的,以及请求究竟是如何发送的
原生代码调用JS
在flushMessageQueue这个方法中,进行了responseId的判断,这个responseId存储在OC代码调用JS时由sendData方法维护的字典中,在OC的请求发出后,看一下JS代码的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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);
}
}
}
}
打开WebViewJavascriptBridge_js这个文件,把里面的JS代码复制一下,用VSCode看,Xcode的高亮属实拉胯,可以看到有一个_dispatchMessageFromObjC方法,看名字就知道是处理OC调用的,它调用了_doDispatchMessageFromObjC方法,在这个方法里面快速浏览一下,发现此方法的实现与OC中的 flushMessageQueue 方法是高度对应的,看来OC调用JS也是用几乎一样的方式。
- 判断responseId
- 判断callbackID
- 调用handler
此外,上一篇文章中曾经说到的与 kQueueHasMessage 属性吻合的URL,接收到这种URL后由OC执行的一行JS代码
1
WebViewJavascriptBridge._fetchQueue();
这行代码做了什么呢?看一下_fetchQuene的实现
1
2
3
4
5
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
这个sendMessageQueue中存储的是JS端每次调用_doSend方法存储的请求,被序列化后返回给OC,OC再调用flushMessageQueue处理。
_doSend方法的功能和OC端的sendData方法是一致的。
看到这里应该比较清楚了,OC端和JS端的实现是高度统一的,他们都是在做这几件事情
- 存储请求
- 维护callback字典(对象)
- 处理另一端的请求
请求的发送
在WebViewJavaScriptBridge的GitHub页面上,操作中需要将以下JS代码写入网页中
1
2
3
4
5
6
7
8
9
10
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
这里创建了一个不可见的iframe,src属性是https://__bridge_loaded__
,此时回头看一下之前被忽略的属性
1
2
#define kQueueHasMessage @"__wvjb_queue_message__"
#define kBridgeLoaded @"__bridge_loaded__"
再加上OC端的一些拼接操作,这时候我们就豁然开朗了,JS端的请求是靠这个不可见iframe发送的,OC端通过对这个src属性的判断,来识别到这是一个需要搭建JSBridge的网页,把WebViewJavascriptBridge_js中的JS代码注入到网页中,看一下这一段
1
2
3
4
messagingIframe = document.createElement("iframe");
messagingIframe.style.display = "none";
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + "://" + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);
WebViewJavascriptBridge_js中的JS代码又创建了一个kQueueHasMessage的iframe,这个也是不可见的,OC识别它到之后就会进行_fetchQueue方法的调用,然后再对返回的请求进行处理。
由此,WebViewJavaScriptBridge就实现了对请求的拦截与分发处理。可以开始进行handler的注册与呼叫,也可以完成请求的回调,真正的搭建起了OC与JS之间的桥梁。
总结
这篇讲了OC是如何调用JS代码的,也讲了二者之间的桥梁是如何搭建的,但都需要配合源码阅读,只看文章会一头雾水。到此,WebViewJavaScriptBridge的实现已经基本说完,没说到的可以看其他部分源码。