WebViewJavascriptBridge是一个Objective-C与JavaScript进行消息互通的三方库。通过WebViewJavascriptBridge,我们可以很方便的实现OC和Javascript互调的功能。WebViewJavascriptBridge实现互调的过程也容易理解,就是在OC环境和Javascript环境各自保存一个相互调用的bridge对象,每一个调用之间都有id和callbackid来找到两个环境对应的处理。从Github上下载源码之后,可以看到核心类主要包含如下几个:

  • WebViewJavascriptBridge_JS:Javascript环境的Bridge初始化和处理。负责接收OC发给Javascript的消息,并且把Javascript环境的消息发送给OC。
  • WKWebViewJavascriptBridge/WebViewJavascriptBridge:主要负责OC环境的消息处理,并且把OC环境的消息发送给Javascript环境。
  • WebViewJavascriptBridgeBase:主要实现了OC环境的Bridge初始化和处理。

下面我们来详细看一下具体的实现(这里的介绍以WKWebView环境为例)。

1.初始化

1.1OC环境的初始化

  1. WebViewJavascriptBridge *bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView ]; //初始化调用
  2.  
  3. //初始化一个OC环境的桥WKWebViewJavascriptBridge并且初始化
  4. + (instancetype)bridgeForWebView:(WKWebView*)webView {
  5. WKWebViewJavascriptBridge* bridge = [[self alloc] init];
  6. [bridge _setupInstance:webView];
  7. [bridge reset];
  8. return bridge;
  9. }
  10.  
  11. //初始化
  12. - (void) _setupInstance:(WKWebView*)webView {
  13. _webView = webView;
  14. _webView.navigationDelegate = self;
  15. _base = [[WebViewJavascriptBridgeBase alloc] init];
  16. _base.delegate = self;
  17. }
  18.  
  19. - (id)init {
  20. if (self = [super init]) {
  21. //用于保存OC环境注册的方法,key是方法名,value是这个方法对应的回调block
  22. self.messageHandlers = [NSMutableDictionary dictionary];
  23. //用于保存交互过程中需要发送给javascirpt环境的消息
  24. self.startupMessageQueue = [NSMutableArray array];
  25. //用于保存OC和javascript环境相互调用的回调模块。通过_uniqueId加上时间戳来确定每个调用的回调
  26. self.responseCallbacks = [NSMutableDictionary dictionary];
  27. _uniqueId = ;
  28. }
  29. return self;
  30. }

【结论】:所有与Javascript之间交互的信息都存储在messageHandlers和responseCallbacks中。这两个属性记录了OC环境与Javascript交互的信息。

1.2OC环境注册方法

注册一个OC方法给Javascript调用,并且把它的回调实现保存在messageHandlers中。具体代码如下:

  1. //注册OC方法,以供Javascript调用
  2. [self.bridge registerHandler:@"testClientCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
  3. NSLog(@"Javascript传递数据: %@", data);
  4. responseCallback(@"OC发给JS的返回值");
  5. }];
  6.  
  7. - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
  8. _base.messageHandlers[handlerName] = [handler copy];
  9. }

1.3Javascript初始化&注册方法

这个我们先来看一下WebViewJavascriptBridge上的示例ExampleAPP.html:

  1. function setupWebViewJavascriptBridge(callback) {
  2. //第一次调用这个方法的时候,为false
  3. if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
  4. //第一次调用的时候,为false
  5. if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
  6. //把callback对象赋值给对象
  7. window.WVJBCallbacks = [callback];
  8. //加载WebViewJavascriptBridge_JS中的代码
  9. var WVJBIframe = document.createElement('iframe');
  10. WVJBIframe.style.display = 'none';
  11. WVJBIframe.src = 'https://__bridge_loaded__';
  12. document.documentElement.appendChild(WVJBIframe);
  13. setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
  14. }
  15. //驱动所有hander的初始化
  16. setupWebViewJavascriptBridge(function(bridge) {
  17. //把WEB中要注册的方法注册到bridge里面
  18. bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
  19. //OC中传过来的数据
  20. log('ObjC called testJavascriptHandler with', data);
  21. //JS返回数据
  22. var responseData = { 'Javascript Says':'Right back atcha!' };
  23. responseCallback(responseData);
  24. })
  25. })

【说明】:调用setupWebViewJavascriptBridge函数,并且这个函数传入的参数也是一个函数。参数函数中有在Javascript环境中注册的setupWebViewJavascriptBridge的实现过程中,我们可以发现,如果不是第一次初始化,会通过window.WVJBCallbacks两个判断返回。iframe可以理解为webview中的窗口,当我们改变iframe的src属性的时候,相当于我们浏览器实现了链接的跳转。比如从www.google.com。下面这段代码的目的就是实现一个到https://__bridge_loaded__的跳转。从而达到初始化Javascript环境的bridge的作用。

  1. //加载WebViewJavascriptBridge_JS中的代码
  2. var WVJBIframe = document.createElement('iframe');
  3. WVJBIframe.style.display = 'none';
  4. WVJBIframe.src = 'https://__bridge_loaded__';
  5. document.documentElement.appendChild(WVJBIframe);
  6. setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)

我们知道,只要webview有跳转,就会调用webview的代理方法,我们重点看下面这个代理方法。

  1. - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
  2. if (webView != _webView) { return; }
  3. NSURL *url = navigationAction.request.URL;
  4. __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
  5. //如果是WebViewJavascriptBridge发送或者接收的消息,则特殊处理。否则按照正常流程处理。
  6. if ([_base isWebViewJavascriptBridgeURL:url]) {
  7. //1第一次注入JS代码
  8. if ([_base isBridgeLoadedURL:url]) {
  9. [_base injectJavascriptFile];
  10. } else if ([_base isQueueMessageURL:url]) {//处理WEB发过来的消息
  11. [self WKFlushMessageQueue];
  12. } else {
  13. [_base logUnkownMessage:url];
  14. }
  15. decisionHandler(WKNavigationActionPolicyCancel);
  16. return;
  17. }
  18. //webview的正常代理执行流程
  19. if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
  20. [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
  21. } else {
  22. decisionHandler(WKNavigationActionPolicyAllow);
  23. }
  24. }

上面的方法中,首先通过isWebViewJavascriptBridgeURL 来判断是普通的跳转还是webViewJavascriptBridge跳转。如果是__bridge_loaded__ 表示是初始化Javascript环境的消息;如果是__WVJB_QUEUE_MESSAGE__ 表示是发送Javascript消息。我们继续看几个核心方法的实现:

  1. #define kOldProtocolScheme @"wvjbscheme"
  2. #define kNewProtocolScheme @"https"
  3. #define kQueueHasMessage @"__wvjb_queue_message__"
  4. #define kBridgeLoaded @"__bridge_loaded__"
  5.  
  6. //是否是WebViewJavascriptBridge框架相关的链接
  7. - (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url {
  8. if (![self isSchemeMatch:url]) {
  9. return NO;
  10. }
  11. return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url];
  12. }
  13. //是否是WebViewJavascriptBridge发送或者接收的消息
  14. - (BOOL)isSchemeMatch:(NSURL*)url {
  15. NSString* scheme = url.scheme.lowercaseString;
  16. return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme];
  17. }
  18. //是WebViewJavascriptBridge发送的消息还是WebViewJavascriptBridge的初始化消息
  19. - (BOOL)isQueueMessageURL:(NSURL*)url {
  20. NSString* host = url.host.lowercaseString;
  21. return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage];
  22. }
  23. //是否是https://__bridge_loaded__这种初始化加载消息
  24. - (BOOL)isBridgeLoadedURL:(NSURL*)url {
  25. NSString* host = url.host.lowercaseString;
  26. return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];
  27. }

接下来调用injectJavascriptFile方法,将WebViewJavascriptBridge_JS中的方法注入到webview中并且执行,从而达到初始化Javascript环境的brige的作用。

  1. - (void)injectJavascriptFile {
  2. NSString *js = WebViewJavascriptBridge_js();
  3. //把javascript代码注入webview中执行
  4. [self _evaluateJavascript:js];
  5. //javascript环境初始化完成以后,如果有startupMessageQueue消息,则立即发送消息
  6. if (self.startupMessageQueue) {
  7. NSArray* queue = self.startupMessageQueue;
  8. self.startupMessageQueue = nil;
  9. for (id queuedMessage in queue) {
  10. [self _dispatchMessage:queuedMessage];
  11. }
  12. }
  13. }
  14.  
  15. //把javascript代码写入webview
  16. - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
  17. [_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
  18. return NULL;
  19. }

那么WebViewJavascriptBridge_JS到底是怎么实现的呢?

  1. NSString * WebViewJavascriptBridge_js() {
  2. #define __wvjb_js_func__(x) #x
  3.  
  4. // BEGIN preprocessorJSCode
  5. static NSString * preprocessorJSCode = @__wvjb_js_func__(
  6. ;(function() {
  7. //如果已经初始化了,则返回
  8. if (window.WebViewJavascriptBridge) {
  9. return;
  10. }
  11.  
  12. if (!window.onerror) {
  13. window.onerror = function(msg, url, line) {
  14. console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line);
  15. }
  16. }
  17. //初始化Bridge对象,OC可以通过WebViewJavascriptBridge来调用JS里面的各种方法
  18. window.WebViewJavascriptBridge = {
  19. registerHandler: registerHandler, //JS中注册方法
  20. callHandler: callHandler, //JS中调用OC中的方法
  21. disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
  22. _fetchQueue: _fetchQueue, //把消息转换成JSON串
  23. _handleMessageFromObjC: _handleMessageFromObjC //OC调用JS的入口方法
  24. };
  25.  
  26. var messagingIframe;
  27. //用于存储消息列表
  28. var sendMessageQueue = [];
  29. //用于存储消息
  30. var messageHandlers = {};
  31. //通过下面两个协议组合来确定是否是特定的消息,然后拦击
  32. var CUSTOM_PROTOCOL_SCHEME = 'https';
  33. var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
  34. //oc调用js的回调
  35. var responseCallbacks = {};
  36. //消息对应的id
  37. var uniqueId = 1;
  38. //是否设置消息超时
  39. var dispatchMessagesWithTimeoutSafety = true;
  40. //web端注册一个消息方法
  41. function registerHandler(handlerName, handler) {
  42. messageHandlers[handlerName] = handler;
  43. }
  44. //web端调用一个OC注册的消息
  45. function callHandler(handlerName, data, responseCallback) {
  46. if (arguments.length == 2 && typeof data == 'function') {
  47. responseCallback = data;
  48. data = null;
  49. }
  50. _doSend({ handlerName:handlerName, data:data }, responseCallback);
  51. }
  52. function disableJavscriptAlertBoxSafetyTimeout() {
  53. dispatchMessagesWithTimeoutSafety = false;
  54. }
  55. //把消息从JS发送到OC,执行具体的发送操作
  56. function _doSend(message, responseCallback) {
  57. if (responseCallback) {
  58. var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
  59. //存储消息的回调ID
  60. responseCallbacks[callbackId] = responseCallback;
  61. //把消息对应的回调ID和消息一起发送,以供消息返回以后使用
  62. message['callbackId'] = callbackId;
  63. }
  64. //把消息放入消息列表
  65. sendMessageQueue.push(message);
  66. //下面这句话会出发JS对OC的调用,让webview执行跳转操作,从而可以在
  67. //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
  68. //中拦截到JS发给OC的消息
  69. messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  70. }
  71. //把消息转换成JSON字符串返回
  72. function _fetchQueue() {
  73. var messageQueueString = JSON.stringify(sendMessageQueue);
  74. sendMessageQueue = [];
  75. return messageQueueString;
  76. }
  77. //处理从OC返回的消息
  78. function _dispatchMessageFromObjC(messageJSON) {
  79. if (dispatchMessagesWithTimeoutSafety) {
  80. setTimeout(_doDispatchMessageFromObjC);
  81. } else {
  82. _doDispatchMessageFromObjC();
  83. }
  84.  
  85. function _doDispatchMessageFromObjC() {
  86. var message = JSON.parse(messageJSON);
  87. var messageHandler;
  88. var responseCallback;
  89. //回调
  90. if (message.responseId) {
  91. responseCallback = responseCallbacks[message.responseId];
  92. if (!responseCallback) {
  93. return;
  94. }
  95. responseCallback(message.responseData);
  96. delete responseCallbacks[message.responseId];
  97. } else { //主动调用
  98. if (message.callbackId) {
  99. var callbackResponseId = message.callbackId;
  100. responseCallback = function(responseData) {
  101. _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
  102. };
  103. }
  104. //获取JS注册的函数
  105. var handler = messageHandlers[message.handlerName];
  106. if (!handler) {
  107. console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
  108. } else { //调用JS中的对应函数处理
  109. handler(message.data, responseCallback);
  110. }
  111. }
  112. }
  113. }
  114. //OC调用JS的入口方法
  115. function _handleMessageFromObjC(messageJSON) {
  116. _dispatchMessageFromObjC(messageJSON);
  117. }
  118.  
  119. messagingIframe = document.createElement('iframe');
  120. messagingIframe.style.display = 'none';
  121. messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  122. document.documentElement.appendChild(messagingIframe);
  123.  
  124. //注册_disableJavascriptAlertBoxSafetyTimeout方法,让OC可以关闭回调超时,默认是开启的
  125. registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
  126. //执行_callWVJBCallbacks方法
  127. setTimeout(_callWVJBCallbacks, 0);
  128.  
  129. //初始化WEB中注册的方法。这个方法会把WEB中的hander注册到bridge中
  130. //下面的代码其实就是执行WEB中的callback函数
  131. function _callWVJBCallbacks() {
  132. var callbacks = window.WVJBCallbacks;
  133. delete window.WVJBCallbacks;
  134. for (var i=0; i<callbacks.length; i++) {
  135. callbacks[i](WebViewJavascriptBridge);
  136. }
  137. }
  138. })();
  139. ); // END preprocessorJSCode
  140.  
  141. #undef __wvjb_js_func__
  142. return preprocessorJSCode;
  143. };

从上面可以看到,整个类就是一个立即执行的Javascript方法。

  • 首先会初始化一个WebViewJavascriptBridge对象,并且这个对象是赋值给window对象,这里的window对象可以理解为webview。所以如果在OC环境中要调用js方法,就可以通过在加上具体方法来调用。
  • WebViewJavascriptBridge对象中有Javascript环境注入的提供给OC调用的方法registerHandler,javascript调用OC环境方法的callHandler。
  • _fetchQueue这个方法的作用就是把Javascript环境的方法序列化成JSON字符串,然后传入OC环境再转换。
  • _handleMessageFromObjC就是处理OC发给Javascript环境的方法。

在这个文件中也初始化了一个iframe实现webview的url跳转功能,从而激发webview代理方法的调用。

  1. messagingIframe = document.createElement('iframe');
  2. messagingIframe.style.display = 'none';
  3. messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  4. document.documentElement.appendChild(messagingIframe);

上面的src就是https://__wvjb_queue_message__/。这个是javascript发送的OC的第一条消息,目的和上面OC环境的startupMessageQueue一样,就是在javascript环境初始化完成以后,把javascript要发送给OC的消息立即发送出去。

1.4OC发消息给Javascript

  1. - (void)p_callJSHandler:(id)sender {
  2. id data = @{ @"OC调用JS方法": @"OC调用JS方法的参数" };
  3. [self.bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {
  4. NSLog(@"JS 响应数据: %@", response);
  5. }];
  6. }
  7.  
  8. /**
  9. OC调用JS方法
  10.  
  11. @param handlerName JS中提供的方法名称
  12. @param data 参数
  13. @param responseCallback 回调block
  14. */
  15. - (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
  16. [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
  17. }
  18.  
  19. - (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
  20. //所有信息存入的字典
  21. NSMutableDictionary* message = [NSMutableDictionary dictionary];
  22.  
  23. if (data) {
  24. message[@"data"] = data;
  25. }
  26.  
  27. if (responseCallback) {
  28. NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
  29. self.responseCallbacks[callbackId] = [responseCallback copy];
  30. message[@"callbackId"] = callbackId;
  31. }
  32.  
  33. if (handlerName) {
  34. message[@"handlerName"] = handlerName;
  35. }
  36. [self _queueMessage:message];
  37. }
  38.  
  39. //把OC消息序列化、并且转化为JS环境的格式,然后在主线程中调用_evaluateJavascript
  40. - (void)_dispatchMessage:(WVJBMessage*)message {
  41. NSString *messageJSON = [self _serializeMessage:message pretty:NO];
  42. [self _log:@"SEND" json:messageJSON];
  43. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
  44. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
  45. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
  46. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
  47. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
  48. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
  49. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
  50. messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
  51.  
  52. NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
  53. if ([[NSThread currentThread] isMainThread]) {
  54. [self _evaluateJavascript:javascriptCommand];
  55.  
  56. } else {
  57. dispatch_sync(dispatch_get_main_queue(), ^{
  58. [self _evaluateJavascript:javascriptCommand];
  59. });
  60. }
  61. }

打印javascriptCommand,结果如下:

  1. WebViewJavascriptBridge._handleMessageFromObjC('{\"callbackId\":\"objc_cb_1\",\"data\":{\"OC调用JS方法\":\"OC调用JS方法的参数\"},\"handlerName\":\"testJavascriptHandler\"}');

实际上就是执行JS环境中的_handleMessageFromObjC方法:

  1. //处理从OC返回的消息
  2. function _dispatchMessageFromObjC(messageJSON) {
  3. if (dispatchMessagesWithTimeoutSafety) {
  4. setTimeout(_doDispatchMessageFromObjC);
  5. } else {
  6. _doDispatchMessageFromObjC();
  7. }
  8.  
  9. function _doDispatchMessageFromObjC() {
  10. var message = JSON.parse(messageJSON);
  11. var messageHandler;
  12. var responseCallback;
  13. //回调
  14. if (message.responseId) {
  15. responseCallback = responseCallbacks[message.responseId];
  16. if (!responseCallback) {
  17. return;
  18. }
  19. responseCallback(message.responseData);
  20. delete responseCallbacks[message.responseId];
  21. } else { //主动调用
  22. if (message.callbackId) {
  23. var callbackResponseId = message.callbackId;
  24. responseCallback = function(responseData) {
  25. _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
  26. };
  27. }
  28. //获取JS注册的函数
  29. var handler = messageHandlers[message.handlerName];
  30. if (!handler) {
  31. console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
  32. } else { //调用JS中的对应函数处理
  33. handler(message.data, responseCallback);
  34. }
  35. }
  36. }
  37. }
  38.  
  39. //把消息从JS发送到OC,执行具体的发送操作
  40. function _doSend(message, responseCallback) {
  41. if (responseCallback) {
  42. var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
  43. //存储消息的回调ID
  44. responseCallbacks[callbackId] = responseCallback;
  45. //把消息对应的回调ID和消息一起发送,以供消息返回以后使用
  46. message['callbackId'] = callbackId;
  47. }
  48. //把消息放入消息列表
  49. sendMessageQueue.push(message);
  50. //下面这句话会出发JS对OC的调用,让webview执行跳转操作,从而可以在
  51. //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
  52. //中拦截到JS发给OC的消息
  53. messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  54. }

最重要的是最后面的通过改变iframe的messagingIframe.src,只有这样才能触发webview的代理方法webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler从而在OC中处理javascript环境触发过来的回调。

  1. //第一次注入JS代码
  2. if ([_base isBridgeLoadedURL:url]) {
  3. [_base injectJavascriptFile];
  4. } else if ([_base isQueueMessageURL:url]) {//处理WEB发过来的消息
  5. [self WKFlushMessageQueue];
  6. } else {
  7. [_base logUnkownMessage:url];
  8. }
  9. decisionHandler(WKNavigationActionPolicyCancel);

这里会走[self WKFlushMessageQueue];方法:

  1. //把消息或者WEB回调发送到OC
  2. - (void)WKFlushMessageQueue {
  3. [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
  4. if (error != nil) {
  5. NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
  6. }
  7. [_base flushMessageQueue:result];
  8. }];
  9. }

最后执行flushMessageQueue,通过Javascript传过来的responseId获取对应的WVJBResponseCallback,执行这个block。到这里从OC发送消息到Javascript并且Javascript返回消息给OC的流程就完成了。

1.5JS发消息给OC

  1. bridge.callHandler('testObjcHandler', {'foo': 'bar'}, function(response) {
  2. log('JS got response', response)-->
  3. })
  4.  
  5. //web端调用一个OC注册的消息
  6. function callHandler(handlerName, data, responseCallback) {
  7. if (arguments.length == && typeof data == 'function') {
  8. responseCallback = data;
  9. data = null;
  10. }
  11. _doSend({ handlerName: handlerName, data: data }, responseCallback);
  12. }
  13.  
  14. //JS调用OC方法,执行具体的发送操作
  15. function _doSend(message, responseCallback) {
  16. if (responseCallback) {
  17. var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
  18. //存储消息的回调ID
  19. responseCallbacks[callbackId] = responseCallback;
  20. //把消息对应的回调ID和消息一起发送,以供消息返回以后使用
  21. message['callbackId'] = callbackId;
  22. }
  23. //把消息放入消息列表
  24. sendMessageQueue.push(message);
  25. //下面这句话会发起JS对OC的调用,让webview执行跳转操作,从而可以在
  26. //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
  27. //中拦截到JS发给OC的消息
  28. messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  29. }

具体执行和OC调用javascript过程一样。

2.总结

  • 分别在OC环境和Javascript环境都保存一个bridge对象,里面维持着requestId、callbackId,以及每个id对应的具体实现。
  • OC通过Javascript环境的window.WebViewJavascriptBridge对象来找到具体的方法,然后执行。
  • Javascript通过改变iframe的src来唤起webview的代理方法webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler从而实现把Javascript消息发送给OC的功能。

WebViewJavascriptBridge浅析的更多相关文章

  1. 浅析hybrid模式下地支付宝钱包和微信

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VuY2hhbzEyNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  2. SQL Server on Linux 理由浅析

    SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...

  3. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  4. 高性能IO模型浅析

    高性能IO模型浅析 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking  ...

  5. netty5 HTTP协议栈浅析与实践

      一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...

  6. Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

    前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...

  7. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

  8. 从源码浅析MVC的MvcRouteHandler、MvcHandler和MvcHttpHandler

    熟悉WebForm开发的朋友一定都知道,Page类必须实现一个接口,就是IHttpHandler.HttpHandler是一个HTTP请求的真正处理中心,在HttpHandler容器中,ASP.NET ...

  9. 【深入浅出jQuery】源码浅析2--奇技淫巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

随机推荐

  1. Java 身份证判断性别获取年龄

    import com.alibaba.fastjson.JSON; import org.junit.Test; import java.text.SimpleDateFormat; import j ...

  2. SpringMVC 请求全过程漫谈

    SpringMVC 请求全过程漫谈 SpringMVC 跟其他的mvc框架一样,如 struts,webwork, 本质上都是 将一个 http 请求(request)进行各种处理, 然后返回resp ...

  3. Android开发中常见的设计模式(四)——策略模式

    策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变换. 假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等 ...

  4. vscode的环境变量code

    vscode的安装路径 本质:vscode的安装路径/Applications/Visual Studio Code.app/Contents/Resources/app/bin 下面有code可执行 ...

  5. JS 正则表达式基本语法(精粹)

    1.正则表达式基本语法 两个特殊的符号'^'和'$'.他们的作用是分别指出一个字符串的开始和结束. 例子如下: "^The":表示所有以"The"开始的字符串( ...

  6. c++冒号作用

    转自http://www.360doc.com/content/13/0605/11/3373961_290615318.shtml 1.冒号(:)用法 (1)表示机构内位域的定义(即该变量占几个bi ...

  7. 把Gitlab迁移到Docker容器里

    把Gitlab迁移到Docker容器里 Apr 9, 2015. | By: 任怀林 公司的gitlab一直是运行在ovm的虚拟机里的,版本还是6.7.5.版本有点老了,最近在研究docker,于是想 ...

  8. CSS表单3 光标样式 (每个位置鼠标放上去的样式不同)

    <!DOCTYPE html> <html>     <head>         <title>单选按钮对齐</title>        ...

  9. python21期day01笔记总结

    2019.3.27 S21 day01笔记总结 一.计算机基础知识 1.计算机组成 用户 应用软件程序开发——用到了两个方面: 1语法 : 2解释器.编译器.虚拟机: 操作系统的开发 硬件组成 2.操 ...

  10. animation渐进实现点点点等待效果

    <style>    @keyframes dot {     0% { width: 0; }     33% { width: .2em; }     66% { width: .5e ...