网络编程

当前位置:永利402游戏网站-永利402com官方网站 > 网络编程 > iOS源码分析—WebViewJSBridge

iOS源码分析—WebViewJSBridge

来源:http://www.xtcsyb.com 作者:永利402游戏网站-永利402com官方网站 时间:2019-09-11 14:45

一个OC和JS交互的桥接机制,主要包含3个类,JS端window.WebViewJavascriptBridge,OC端WebViewJavascriptBridge和WebViewJavascriptBridgeBase。桥接类支持JS调用OC方法,OC调用JS方法。JS调用OC通过重定向url并取handlerName来调用,OC调用JS通过stringByEvaluatingJavaScriptFromString调用。

图片 1图片来源于网络.jpg

  1. OC端初始化、注册func:

    • 通过初始化方法生成1个WebViewJavascriptBridge以及WebViewJavascriptBridgeBase。webview的delegate改为WebViewJavascriptBridge,WebViewJavascriptBridge的_webViewDelegate是外部传入的webviewController。

      + (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle{ WebViewJavascriptBridge* bridge = [[self alloc] init]; [bridge _platformSpecificSetup:webView webViewDelegate:webViewDelegate handler:messageHandler resourceBundle:bundle]; return bridge;}-  _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(id<UIWebViewDelegate>)webViewDelegate handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle { _webView = webView; _webView.delegate = self; _webViewDelegate = webViewDelegate; _base = [[WebViewJavascriptBridgeBase alloc] initWithHandler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle]; _base.delegate = self;}
      
    • messageHandler是默认处理函数,当JS调用OC时,根据方法名找不到OC的block时,或者通过send()方法调用OC时,调用该默认messageHandler。

    • messageHandlers是键值对,存放了OC注册的方法以及对应的方法名。startupMessageQueue是数组,存放了JS端WebViewJavascriptBridge.js.txt脚本执行之前,OC调用的JS方法信息message。

      -initWithHandler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle{ self = [super init]; _resourceBundle = bundle; self.messageHandler = messageHandler; self.messageHandlers = [NSMutableDictionary dictionary]; self.startupMessageQueue = [NSMutableArray array]; self.responseCallbacks = [NSMutableDictionary dictionary]; _uniqueId = 0; return;}
      
    • 注册OC方法,调用registerHandler:name handler:(WVJBHandler)handler,往messageHandlers键值对中存。

      - registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { _base.messageHandlers[handlerName] = [handler copy];}
      
  2. JS端初始化、注册func

    • 加载JS脚本

      webView加载完之后,进入webViewDidFinishLoad回调,调用injectJavascriptFile,执行WebViewJavascriptBridge.js脚本。并且把startupMessageQueue中的message批量处理掉。

      - injectJavascriptFile:shouldInject { ... NSString *filePath = [bundle pathForResource:@"WebViewJavascriptBridge.js" ofType:@"txt"]; NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; [self _evaluateJavascript:js]; [self dispatchStartUpMessageQueue];}- dispatchStartUpMessageQueue { if (self.startupMessageQueue) { for (id queuedMessage in self.startupMessageQueue) { [self _dispatchMessage:queuedMessage]; } self.startupMessageQueue = nil; }}
      
    • WebViewJavascriptBridge.js脚本创建window.WebViewJavascriptBridge对象。_handleMessageFromObjC是OC调用JS的入口。

      window.WebViewJavascriptBridge = { init: init, send: send, registerHandler: registerHandler, callHandler: callHandler, _fetchQueue: _fetchQueue, _handleMessageFromObjC: _handleMessageFromObjC}
      
    • 同时创建一些变量,receiveMessageQueue是数组,存放了init方法调用之前,OC调用JS的信息Message队列。messageHandlers是键值对,存放JS端注册的方法。sendMessageQueue是Message队列,存放JS调用OC的方法。

      var messagingIframevar sendMessageQueue = []var receiveMessageQueue = []var messageHandlers = {}
      
    • 注册JS响应方法,调用registerHandler方法注册。

      function registerHandler(handlerName, handler) { messageHandlers[handlerName] = handler}
      
    • init方法初始化,同时messageHandler是初始化完的回调block,处理默认逻辑,和OC的messageHandler功能一样。同时批量处理receiveMessageQueue中的消息。

      function init(messageHandler) { WebViewJavascriptBridge._messageHandler = messageHandler var receivedMessages = receiveMessageQueue receiveMessageQueue = null for (var i=0; i<receivedMessages.length; i++) { _dispatchMessageFromObjC(receivedMessages[i]) }}
      
  3. OC端调用JS端func:

    • 调用callHandler:handlerName data:data responseCallback:responseCallback方法或者send:data responseCallback:responseCallback方法来调用JS方法。内部调用WebViewJavascriptBridgeBase的sendData:data responseCallback:responseCallback handlerName:handlerName方法。

      - sendData:data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { NSMutableDictionary* message = [NSMutableDictionary dictionary]; if  { 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];}
      
    • 如果responseCallback,则创建一个responseCallback,维护到responseCallbacks键值对中。包装一个Message对象,包括data、handlerName和callbackId,一起发送。如果WebViewJavascriptBridge.js未加载,则往startupMessageQueue队列里面添加。如果已加载,则发送给window.WebViewJavascriptBridge对象。

      - _queueMessage:(WVJBMessage*)message { if (self.startupMessageQueue) { [self.startupMessageQueue addObject:message]; } else { [self _dispatchMessage:message]; }}- _dispatchMessage:(WVJBMessage*)message { ... NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC;", messageJSON]; [self _evaluateJavascript:javascriptCommand];}
      
    • 执行_handleMessageFromObjC方法,如果WebViewJavascriptBridge未init初始化,则添加到receiveMessageQueue队列,否则执行dispatchMessageFromObjC。

      function _handleMessageFromObjC(messageJSON) { if (receiveMessageQueue) { receiveMessageQueue.push(messageJSON) } else { _dispatchMessageFromObjC(messageJSON) }}
      
    • dispatchMessageFromObjC,如果有callBackId,则创建一个回调func,通过doSend回调OC,如果没有handlerName,则调用默认的messageHandler,传data。

      function _dispatchMessageFromObjC(messageJSON) { if (message.callbackId) { var callbackResponseId = message.callbackId responseCallback = function(responseData) { _doSend({ responseId:callbackResponseId, responseData:responseData }) } } var handler = WebViewJavascriptBridge._messageHandler if (message.handlerName) { handler = messageHandlers[message.handlerName] } handler(message.data, responseCallback)}
      
    • 处理回调,js方法调用responseCallback(),传入responseId和responseData,调用doSend方法。doSend方法首先构建一个Message,放入sendMessageQueue中,然后重定向一下scheme://message。

      function _doSend(message, responseCallback) { ... sendMessageQueue.push messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE}
      
    • OC端的WebViewJavascriptBridge拦截到url,获取sendMessageQueue数组中的message,执行flushMessageQueue:message方法。

      - webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { ... NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; [_base flushMessageQueue:messageQueueString]; ...}
      
    • flushMessageQueue:message方法里判断,如果有responseId,则通过responseId从_responseCallbacks键值对中取出OC的回调方法,并且把responseData传给回调方法。

      - flushMessageQueue:(NSString *)messageQueueString{ for (WVJBMessage* message in messages) { NSString* responseId = message[@"responseId"]; if (responseId) { WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; responseCallback(message[@"responseData"]); } }}
      
  4. JS端调用OC端func:

    • JS调用send和callHandler,然后调用doSend()方法

      _doSend({ handlerName:handlerName, data:data }, responseCallback)
      
    • _doSend方法中,如果有回调responseCallback,则维护回调responseCallbacks,存放responseCallback。sendMessageQueue里添加message。然后重定向Url。

      function _doSend(message, responseCallback) { if (responseCallback) { var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime() responseCallbacks[callbackId] = responseCallback message['callbackId'] = callbackId } sendMessageQueue.push messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE}
      
    • OC端的shouldStartLoadWithRequest:方法拦截到url,通过WebViewJavascriptBridge._fetchQueue()获取message队列,然后调用flushMessageQueue方法处理message。

      - webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; [_base flushMessageQueue:messageQueueString]; return NO;}
      
    • flushMessageQueue:messageQueue方法,如果有callbackId,则调用_queueMessage方法,传递回调数据以及responseId。如果有handlerName,则调用相应的OC方法,否则调用默认方法messageHandler。

      - flushMessageQueue:(NSString *)messageQueueString{ NSString* callbackId = message[@"callbackId"]; if (callbackId) { responseCallback = ^(id responseData) { if (responseData == nil) { responseData = [NSNull null]; } WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; [self _queueMessage:msg]; }; } WVJBHandler handler; if (message[@"handlerName"]) { handler = self.messageHandlers[message[@"handlerName"]]; } else { handler = self.messageHandler; } handler(message[@"data"], responseCallback);}
      
    • _queueMessage方法,调用dispatchMessage方法,接着调用WebViewJavascriptBridge.handleMessageFromObjC方法。

      - _dispatchMessage:(WVJBMessage*)message { NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC;", messageJSON]; [self _evaluateJavascript:javascriptCommand];}
      
    • _handleMessageFromObjC调用dispatchMessageFromObjC(messageJSON)方法,如果message有responseId,则通过responseId和responseCallbacks找到JS回调方法,执行。

      function _dispatchMessageFromObjC(messageJSON) { if (message.responseId) { responseCallback = responseCallbacks[message.responseId] if (!responseCallback) { return; } responseCallback(message.responseData) delete responseCallbacks[message.responseId] }}
      
WebViewJavaScriptBridge是被使用最多,也是最好用的移动端与JS交互的第三方框架,在此总结一下它的使用方法和内部原理。

​ WebViewJavascriptBridge提供了一种native和js交互的思路,且十分轻量。

一.使用方法
iOS端(这里以UIWebView为例)

1.导入头文件

#import "WebViewJavascriptBridge.h"

2.声明桥接对象

@property WebViewJavascriptBridge* bridge;

3.初始化桥接对象

self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView];

4.注册函数

[self.bridge registerHandler:@"getUserInfo" handler:^(id data, WVJBResponseCallback responseCallback) { NSDictionary *dict = @{ @"userid":@"0001" }; responseCallback; }];@"getUserInfo":与JS端共同商定的函数名,JS在调用时函数名必须一致data:函数调用时传递过来的数据responseCallback:函数被调用时传递过来的回调函数

4.调用函数

[self.javaScriptBridge callHandler:@"webViewRefresh" data:nil responseCallback:^(id responseData) { }];@"webViewRefresh":与JS端共同商定的函数名,JS在注册时函数名必须一致data:调用函数时传递过去的数据responseCallback:调用函数时传递过去的回调函数responseData:传递过去的回调函数被JS调用时所传递过来的数据
JS端

1.声明一个函数,将此函数复制到JS文件中

function setupWebViewJavascriptBridge { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push; } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement; WVJBIframe.style.display = 'none'; WVJBIframe.src = 'https://__bridge_loaded__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)}

2.注册或者调用交互方法

setupWebViewJavascriptBridge(function { /* Initialize your app here */ bridge.registerHandler('JS Echo', function(data, responseCallback) { console.log("JS Echo called with:", data) responseCallback bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) { console.log("JS received response:", responseData) })})
二.内部原理

1.框架文件

图片 220.pic.jpg

① WebViewJavascriptBridgeBase中声明和实现了基础的方法和属性② WebViewJavascriptBridge_js中是一段JS代码,在初始化时编译,用于交互③ WebViewJavascriptBridge和WKWebViewJavascriptBridge是分别适应UIWebView和WKWebView的继承于WebViewJavascriptBridgeBase的子类2.初始化

图片 31.pic.jpg图片 42.pic.jpg

在JS文件被加载时会执行setupWebViewJavascriptBridge的方法,改变iframe.src,就会发起页面请求,从而被UIWebView的代理方法拦截到,执行injectJavascriptFile,编译WebViewJavascriptBridge_js,为后面的交互做好准备,这就是初始化的过程。

function setupWebViewJavascriptBridge { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push; } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement; WVJBIframe.style.display = 'none'; WVJBIframe.src = 'https://__bridge_loaded__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)}

3.JS调用OC函数原理(这里以UIWebView为例)首先需要OC注册函数

[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"testObjcCallback called: %@", data); responseCallback(@"Response from testObjcCallback"); }];

OC注册函数需要调用WebViewJavascriptBridge的方法

- registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;

那么看一下它的做了什么

- registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { _base.messageHandlers[handlerName] = [handler copy];}

它将handlerName为key,以handler为value储存在一个叫做messageHandlers的字典中,注册就完成了然后需要JS调用OC注册好的函数

bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function { log('JS got response', response) })

JS调用函数需要用到WebViewJavascriptBridge_js的方法

function callHandler(handlerName, data, responseCallback) { if (arguments.length == 2 && typeof data == 'function') { responseCallback = data; data = null; } _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; } sendMessageQueue.push; messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; }

这个方法将handlerName, data, responseCallback储存在message字典中,并将message字典储存在sendMessageQueue数组中,最后改变了iframe.src,发送请求

图片 53.pic.jpg

- (NSString *)webViewJavascriptFetchQueyCommand { return @"WebViewJavascriptBridge._fetchQueue();";}

被被UIWebView的代理方法拦截到之后首先执行JS方法WebViewJavascriptBridge._fetchQueue(),拿到要调用的函数的信息

function _fetchQueue() { var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; return messageQueueString; }

然后执行OC的flushMessageQueue方法

- 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) {//当OC调用JS注册的函数后执行回调方法时才有responseId,此处后面会介绍 WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; responseCallback(message[@"responseData"]); [self.responseCallbacks removeObjectForKey:responseId]; } else {//当JS调用OC注册的函数 WVJBResponseCallback responseCallback = NULL; NSString* callbackId = message[@"callbackId"]; if (callbackId) {//如果有JS的回调 responseCallback = ^(id responseData) { if (responseData == nil) { responseData = [NSNull null]; } //注意此处添加了responseId,之后在WebViewJavascriptBridge_js的_dispatchMessageFromObjC方法中会用到 WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; [self _queueMessage:msg];//执行JS回调函数 }; } else { responseCallback = ^(id ignoreResponseData) { // Do nothing }; } WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];//根据函数名拿到handler if  { NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); continue; } handler(message[@"data"], responseCallback);//执行handler } }}

下面我们再看一下JS的回调函数是如何被执行的

- _queueMessage:(WVJBMessage*)message { if (self.startupMessageQueue) { [self.startupMessageQueue addObject:message]; } else { [self _dispatchMessage:message]; }}- _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"]; //执行JS的_handleMessageFromObjC拿到要执行的回调函数 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的handleMessageFromObjC方法是如何找到相应的回调函数的

function _dispatchMessageFromObjC(messageJSON) { if (dispatchMessagesWithTimeoutSafety) { setTimeout(_doDispatchMessageFromObjC); } else { _doDispatchMessageFromObjC(); } function _doDispatchMessageFromObjC() { var message = JSON.parse(messageJSON); var messageHandler; var responseCallback; if (message.responseId) {//之前OC的flushMessageQueue方法中已经添加了responseId,那么进到这个判断,根据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  { console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); } else { handler(message.data, responseCallback); } } } }

至此,JS调用OC注册的函数流程完结,难点在于responseCallback如何执行,要记住responseCallback是由调用方传入的参数,应由调用方执行。

4.OC调用JS函数原理(这里以UIWebView为例)首先需要JS注册函数

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) })

看一下这个方法的实现,在WebViewJavascriptBridge_js中

function registerHandler(handlerName, handler) { messageHandlers[handlerName] = handler; }

可以看到这里和OC注册函数时的操作一样,都是将handlerName为key,以handler为value储存在一个叫做messageHandlers的字典中,注册就完成了

然后就是OC开始调用这个函数

[_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }];

看一下这个方法的实现,在WebViewJavascriptBridge中

- callHandler:(NSString *)handlerName data:data responseCallback:(WVJBResponseCallback)responseCallback { [_base sendData:data responseCallback:responseCallback handlerName:handlerName];}- sendData:data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { NSMutableDictionary* message = [NSMutableDictionary dictionary]; if  { message[@"data"] = data; } if (responseCallback) {//注意这里,如果有responseCallback,就会callbackId为key存起来 NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId]; self.responseCallbacks[callbackId] = [responseCallback copy]; message[@"callbackId"] = callbackId; } if (handlerName) { message[@"handlerName"] = handlerName; } [self _queueMessage:message];}- _queueMessage:(WVJBMessage*)message { if (self.startupMessageQueue) { [self.startupMessageQueue addObject:message]; } else { [self _dispatchMessage:message]; }}- _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]; }); }}

可见最终还是执行到WebViewJavascriptBridge_js中的handleMessageFromObjC方法才能拿到要执行的JS代码,那我们看一下这里跟JS调用OC函数不同在哪里

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) {//如果有responseCallback,注意之前的处理 var callbackResponseId = message.callbackId;//注意此处它为responseCallback赋值了一个函数,当执行responseCallback时会执行_doSend方法,并且以responseId为key传入了callbackResponseId responseCallback = function(responseData) { _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); }; } //找到相应的handler并执行 var handler = messageHandlers[message.handlerName]; if  { console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); } else { handler(message.data, responseCallback); } } } }

此处关键在于responseCallback是OC传入的代码块,应该由OC执行,所以JS调用_doSend方法改变iframe.src发起请求,通知OC执行responseCallback

function _doSend(message, responseCallback) { if (responseCallback) { var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); responseCallbacks[callbackId] = responseCallback; message['callbackId'] = callbackId; } sendMessageQueue.push; messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; }

然后OC拦截到请求的操作之前已经过了一遍,那不同的地方在哪呢,那就是有了responseId

- 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) {//有了responseId,这很关键,根据responseId找到responseCallback并执行 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  { NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); continue; } handler(message[@"data"], responseCallback); } }}

找到了之前存储的responseCallback并执行,到此OC调用JS注册的函数流程完毕。

三.总结

受限于JS知识水平不足,有些地方分析的不太好,不对之处还望指正,不胜感激!另外我还写了一篇JavaScriptCore的使用介绍,JavaScriptCore是苹果提供的一套用于与JS交互的框架,感兴趣的朋友可以点这里看一下

本文由永利402游戏网站-永利402com官方网站发布于网络编程,转载请注明出处:iOS源码分析—WebViewJSBridge

关键词: