JSBridge的实现与项目中的封装思路(二)

iOS JSBridge

Posted by NKQ on November 3, 2021

上一篇讲到来自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的实现已经基本说完,没说到的可以看其他部分源码。