相互调用的框架分析,WebViewJavaScriptBridge源码剖析
分类:计算机编程

亲,笔者的简书已不复维护和翻新了,全数小说都迁移到了本人的个体博客:

WebViewJavaScriptBridge是IOS中JS和OC交互的常用框架,帮助以下三种调用:

对于应用软件开拓,一方面须求火速迭代,一方面供给功力好品质优。这样Hybrid格局出现。本文重要剖判一下WebViewJavascriptBridge那几个OC与JS交互框架的卷入思路。

创建 Bridge

  (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView;

何况安装 Bridge 为 webview 和 bridgebase 的代理


WebViewJavaScriptBridge是IOS中JS和OC交互的常用框架,它选用block的款型管理回调(相关德姆o已上传),帮忙以下三种调用:

1. OC端的方法如下

此时此刻版本:v5.0.7

OC 调用 JS

主干用法

它的两种采纳情况如下:

图片 1WebViewJavaScriptBridge使用情状图片 2Method Frome OCMethod 1 是登记一个OC的艺术--testObjcCallback,handler是JS掉用的内容,responseCallback是将OC管理回来给JS的回调(对应的是上述第2种调用);Method 2 是调用JS的主意的testJavascriptHandler方法,@{ @"foo":@"before ready" }是亟需传递的参数,responseCallback是将JS管理结果再次回到给OC的回调(对应的是上述的第1种调用)图片 3Method Frome JSMethod 1 是JS注册贰个办法供OC调用,responseCallback(responseData)是将管理结果再次回到OC。Method 2 是在点击了二个开关之后JS调用OC的章程,{'foo': 'bar'}是给OC的参数,response是OC管理后回来给JS的多寡。注:JS中是足以不写;号的,这和swift一样

JS调用OC,OC将管理结果回调给JS:要想被JS调用,我们第一要登记一个handler,和回调的 block,注册时候以键值对的样式积累那么些block,handler,当JS调用OC时调用webView:shouldStartLoadWithRequest:navigationType:这一个方法,遵照JS传来的多寡,找到从前封存的Block并且调用,同时新建二个必要把管理结果回调给JS的Blcok,OC管理达成果过后调用刚才创立的Block利用stringByEvaluatingJavaScriptFromString将管理结果再次回到给JS。OC调用JS时与此类似。基于那些流程,大家来看WebViewJavaScriptBridge的贯彻进度。

图片 4

- setupWVJSBridge { [WebViewJavascriptBridge enableLogging]; _bridge = [WebViewJavascriptBridge bridgeForWebView:_webView]; [_bridge setWebViewDelegate:self]; [_bridge registerHandler:@"Hybird" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"data %@ responseCallBack %@", data, responseCallback); NSDictionary* dicResult = @{@"ret":@"OK"}; responseCallback(dicResult); }]; [_bridge callHandler:@"InvokeJavascriptHandler" data:@{ @"say":@"Hello" } responseCallback:^(id responseData) { NSLog(@"OC get call back from js when OC invoke %@", responseData); }];}

WebViewJavascriptBridge.registerHandler('InvokeJavascriptHandler', function(data, responseCallback) { responseCallback({say : 'Hi'}) }) function test_Hybird(){WebViewJavascriptBridge.callHandler('Hybird', {}, function{ alert('js 收到回调 '   cb.ret) }) }

1. OC 传入方法名 handlerName 和 data(参数),JS 重回 responseCallback

使用数组把 handlerName 和参数 data 归入字典中

使用

- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback 

e.g.
x
[bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
// you can do something here
}

然后在JS 端注册同名的function 以testJavascriptHandler 为例

bridge.registerHandler('testJavascriptHandler',function(data, responseCallback) {
// you can do something here }
)

原理

接下去大家来深入分析从页面加载到OC和JS相互调用的整个进度:

当加载HTML文件的时候调用[webView loadHTMLString:appHtml baseURL:baseURL];,那时会调用:

 - webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (webView != _webView) { return YES; } NSURL *url = [request URL]; __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; 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]; } return NO; } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; } else { return YES; }}

在这几个艺术中决断UCR-VL的品类,假使是WebViewJavascriptBridgeURL那正是说就能够咬定是BridgeLoadedURLQueueMessageURL依然不解的U福睿斯L,在第一回调用时是重返YES的,然后的U奥迪Q7L正是BridgeLoadedURL,大家在看它的论断标准[self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];Scheme是友善安装的https,那么BridgeLoaded(__bridge_loaded__)是怎么着吗?大家看ExampleApp.html文件,开掘它的script标签中有像这种类型一段代码:

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)}setupWebViewJavascriptBridge(function { var uniqueId = 1 function log(message, data) { var log = document.getElementById var el = document.createElement el.className = 'logLine' el.innerHTML = uniqueId     '. '   message   ':<br/>'   JSON.stringify if (log.children.length) { log.insertBefore(el, log.children[0]) } else { log.appendChild } }

在这里大家开采了https://__bridge_loaded__这个iframe的src,况且在接下去调用setupWebViewJavascriptBridge时这个src会作为三个伸手,这时会调用shouldStartLoadWithRequest其一办法。此时就满意了isBridgeLoadedURL那一个乞求。那时就能够调用

[_base injectJavascriptFile]

流入一个JS文件,那个JS文件的要害内容是:

window.WebViewJavascriptBridge = { registerHandler: registerHandler, callHandler: callHandler, disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout, _fetchQueue: _fetchQueue, _handleMessageFromObjC: _handleMessageFromObjC};var messagingIframe;var sendMessageQueue = [];var messageHandlers = {};var CUSTOM_PROTOCOL_SCHEME = 'https';var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';var responseCallbacks = {};var uniqueId = 1;var dispatchMessagesWithTimeoutSafety = true;function registerHandler(handlerName, handler) { messageHandlers[handlerName] = handler;}function callHandler(handlerName, data, responseCallback) { _doSend();}function disableJavscriptAlertBoxSafetyTimeout() { dispatchMessagesWithTimeoutSafety = false;}function _doSend(message, responseCallback) { sendMessageQueue.push; messagingIframe.src = CUSTOM_PROTOCOL_SCHEME   '://'   QUEUE_HAS_MESSAGE;}function _fetchQueue() { var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; return messageQueueString;}function _dispatchMessageFromObjC(messageJSON) { if (dispatchMessagesWithTimeoutSafety) { setTimeout(_doDispatchMessageFromObjC); } else { _doDispatchMessageFromObjC(); } function _doDispatchMessageFromObjC() { } }}function _handleMessageFromObjC(messageJSON) { _dispatchMessageFromObjC(messageJSON);}messagingIframe = document.createElement;messagingIframe.style.display = 'none';messagingIframe.src = CUSTOM_PROTOCOL_SCHEME   '://'   QUEUE_HAS_MESSAGE;document.documentElement.appendChild(messagingIframe);registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);setTimeout(_callWVJBCallbacks, 0);function _callWVJBCallbacks() { var callbacks = window.WVJBCallbacks; delete window.WVJBCallbacks; for (var i=0; i<callbacks.length; i  ) { callbacks[i](WebViewJavascriptBridge); }}

上边我们来分析下流入的JavaScript的从头到尾的经过。

  1. 给window对象增加八性格子WebViewJavascriptBridgeJS中得以平素给指标增加属性),这些目的包涵以下内容:
 1) registerHandler:注册调用方法 2)callHandler:调用OC时的方法 3)disableJavscriptAlertBoxSafetyTimeout:超时时弹框是否展示的标示 4)_fetchQueue:获取Queue对象的方法 5)_handleMessageFromObjC:处理OC调用的方法

2.概念了一层层的变量来囤积数据messagingIframe:iframe标签,当大家的WebView加载它的时候,会调用个中的src,src固然调用恳求的U锐界L。

 1)sendMessageQueue:message数组 2)messageHandlers:handler对象 *JS中{}表示对象* 3)CUSTOM_PROTOCOL_SCHEME:scheme标示 4)QUEUE_HAS_MESSAGE:有Message标识 5)responseCallbacks:回调对象 6)uniqueId:唯一标示ID

进过体系一的分析,大家理解了动用WebViewJavaScriptBridge前供给做的预备工作,那么接下去,大家共同钻探OCJS互相之间调用的切实可行奉行进程以及中间的要领。

Method Frome OC

本来要求提前注入JS代码。

原理

js 已经登记了 以methodName 为代表的形式 OC通过

[_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];

来执行JS语句


二、 JS调用OC,然后OC将管理结果重返JS

OC调用registerHandler:,那时将其调用音讯存款和储蓄在messageHandlers字典中以handlerName为Key,给JS处理结果的Block为Value(_base.messageHandlers[handlerName] = [handler copy]);

JS调用

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

来调用上文OC注册的不二秘技,这么些brige就是上文注入JS代码时候创设的,大家再它个中做了怎么样。

function callHandler(handlerName, data, responseCallback) { if (arguments.length == 2 && typeof data == 'function') { responseCallback = data; data = null; } _doSend({ handlerName:handlerName, data:data }, responseCallback);}

那边推断了参数类型,假使传入的参数独有三个,何况第二个是function花色,那么就将第一个参数变为callBack,data置空,将handlerName和data转化成二个目的的二日性情并传给_doSend()

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

这里的responseCallback是JS先调用OC然后OC调用JS时才会有的,假如这种境况,那么需求用独一的标记(callbackId),来将以此responseCallback存款和储蓄在responseCallbacks中,并且给message添加callbackId 这特个性。本条数值会在后一次OC调用JS的时候作为独一的Key被用到。软后将message放入:sendMessageQueue队列中,然后拼接src。

经过艺术步骤2,会调用上面的回调方法

 - webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{}

在那一个方法中调用

 NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; [_base flushMessageQueue:messageQueueString];

第一得到JS中的messageQueue(步骤第22中学的sendMessageQueue),然后调用flushMessageQueue:方法:

 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]; /////////*********OC先调用了JS,JS再调用了OC*********/////////// NSString* responseId = message[@"responseId"]; if (responseId) { //调用之前存储的Bolck WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; responseCallback(message[@"responseData"]); [self.responseCallbacks removeObjectForKey:responseId]; /////////*********JS先调用OC,OC再调用JS*********/////////// /// 这里是JS先调用OC的时候存储的是 JS的回调函数 } else { // JS先调用的OC,OC再调用JS WVJBResponseCallback responseCallback = NULL; NSString* callbackId = message[@"callbackId"]; if (callbackId) { responseCallback = ^(id responseData) { if (responseData == nil) { responseData = [NSNull null]; } //JS调用OC时候的存储(后续OC调用JS返回计算结果) 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; } //调用OC的Block,同时,如果OC调用responseCallback,则调用_queueMessage进行相应的处理 handler(message[@"data"], responseCallback); }}

此地先将回到的JSON字符串转变来对象,这里的字符串是调用

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

获取的,这里将sendMessageQueue转为JSON,然后将其置空,这里为啥使用数组而不用对象来积存吗?因为可能JS还并未有拍卖完结就有三遍调用,要确定保障她们不扬弃使用了数组。然后推断数组中的Message对象是还是不是有responseId(JS调用OC第二回时存款和储蓄的),这里未有responseId所以走else:如果有callbackId(在JS中作为回调用的),定义responseCallback,这个block正是OC将管理结果重临给JS时用到的block。若无callbackId声明,无需回调JS,这年responseCallback为空。最后调用步骤第11中学蕴藏在messageHandlers目的中的block,并且将刚刚创立的responseCallback作为参数字传送入,以便OC将计算结果传递给JS。

[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"testObjcCallback called: %@", data); responseCallback(@"response form oc's call back"); }];

handler的末段一步调用responseCallback()将管理结果回调给JS。这几个responseCallback()正是大家在步骤3中开创的responseCallback。我们再来看那么些block。看步骤3方可看看这一个其内部调用

 [self _queueMessage:msg]; [self _dispatchMessage:message];

_dispatchMessage中间试行:

 NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC;", messageJSON];

接下来JS中的_handleMessageFromObjC就能吸取到OC传过来处理结果。

 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 { // OC先调用JS是用到 } }

以此时候大家看出了步骤三中的responseId的效果了,那时候responseId就标注了是OC将管理结果传递给JS并没有供给JS再调用OC了,那时只调用responseCallback(message.responseData);将数据传给JS。这样我们就完结了JS调用OC,然后OC将结果回调给JS的全部经过。

Method 1 是挂号一个OC的艺术--testObjcCallback,handler是JS掉用的内容,responseCallback是将OC管理回来给JS的回调(对应的是上述第2种调用);

第一大家驾驭,OC能够在webView中有益的实行JS代码:

JS调用OC语句

OC注册三个桥接

[_bridge registerHandler:@"myHander" handler:^(id data, WVJBResponseCallback responseCallback) {
// you can do something here 

}];

参数有 HanderName 还应该有 回调 和 嵌套回调

js推行有个别方法

bridge.callHandler('myHander',{'foo':'bar'},function(response){
// you can do something here 
    })

三、OC调用JS,然后JS将管理结果再次回到给OC

同OC注册情势时候一样,JS也是用多个messageHandlers目的来囤积

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

 - 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]; }

此处运用message字典来囤积参数,方法名,使用responseCallbacks来存款和储蓄JS管理完事后,要求回调的Block(这里为了保障多次调用不会覆盖在此以前的调用,使用了唯一的callbackId)。同上文所述,最后会调用

 - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand]; }

这时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); } 这里定义了需要给OC传递结果的`responseCallback`,取出之前注册的`handler`:`messageHandlers[message.handlerName]`,然后调用这个`handler`,并将这个`responseCallback`作为参数传进去,`handler(message.data, responseCallback);`

 在步骤三中调用handler: function(data, responseCallback) { log('ObjC called testJavascriptHandler with', data) var responseData = { 'Javascript Says':'Right back atcha!' } log('JS responding with', responseData) responseCallback(responseData) } 在这个`handler`的结尾调用步骤三种的`responseCallback`(传入的只有数据没有回调),根据步骤三可以看出来其会调用`_doSend`方法。该方法中由于没有传进去回调,所以不会给message对象添加`callbackId`,只调用 sendMessageQueue.push; messagingIframe.src = CUSTOM_PROTOCOL_SCHEME   '://'   QUEUE_HAS_MESSAGE;

那是出于含有responseId(在步骤三中的_doSend调用时设置),所以只会收取以前存款和储蓄的block,况兼将结果回传给OC:

 //调用之前存储的Bolck WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; responseCallback(message[@"responseData"]); [self.responseCallbacks removeObjectForKey:responseId];

由来,OC和JS交互的具备逻辑已介绍达成(WKWebView完结情势一样),总括下三种情景的回调,其促成格局及其相似,正如小说最先的下结论。

Method 2 是调用JS的措施的testJavascriptHandler方法,@{ @"foo":@"before ready" }是必要传递的参数,responseCallback是将JS管理结果再次回到给OC的回调(对应的是上述的第1种调用)

[webView stringByEvaluatingJavaScriptFromString:@"alert"];

Javascript调用OC 使用的是 HanderName 对应特定 Block 块(Object)


原理

2. JS端的秘诀如下

而JS直接调用OC未有提供第一手的形式(当然了,iOS7 之后Apple提供了JavaScriptCore框架能够利用,本文不详细介绍),那么我们须要通过触发url重定向成效,在WebView的回调方法中抓获url,然后经过一定的url来施行大家须求的主意。

1、监听

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 

方法

图片 5

作者提供的措施是在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 = 'wvjbscheme://__BRIDGE_LOADED__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0) }setupWebViewJavascriptBridge()

前端同学需求在JS代码中,提前写入这段代码,才会有Hybrid交互的技术。

当实行上边这段代码的时候,会得到iframe这天性情,通过退换iframe的src来触发UIWebview的回调:

- webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

此间注入的回调url是:wvjbscheme://BRIDGE_LOADED顺便说下举办JS的url是:wvjbscheme://WVJB_QUEUE_MESSAGE

2、截获request的URL

NSURL *url = [request URL];

-(BOOL)isCorrectProcotocolScheme:(NSURL*)url {
if([[url scheme] isEqualToString:kCustomProtocolScheme]){
    return YES;
} else {
    return NO;
}
}

-(BOOL)isQueueMessageURL:(NSURL*)url {
if([[url host] isEqualToString:kQueueHasMessage]){
    return YES;
} else {
    return NO;
}
}

Method Frome JS

道理当然是那样的我们OC也得以在手动实践JS注入代码

简易点的措施,直接在修改库文件WebViewJavascriptBridge,把WebViewJavascriptBridgeBase *_base;获得.h文件中,大家就足以一本万利的调用,要求在网页加载完回调中推行。

- webViewDidFinishLoad:(UIWebView *)webView { [_base performSelector:@selector(injectJavascriptFile)];}

只要您不想更换三方库呢,能够用runtime的秘籍,获取那几个私有的变量_base

- injectJSBridgeBase { unsigned int count = 0; Ivar *members = class_copyIvarList([_bridge class], &count); for (int i=0; i<count; i  ) { Ivar var = members[i]; const char * memberName = ivar_getName; const char * memberType = ivar_getTypeEncoding; NSString *strMemberType = [NSString stringWithFormat:@"%s", memberType]; if ([strMemberType rangeOfString:@"WebViewJavascriptBridgeBase"].location != NSNotFound) { _base = object_getIvar(_bridge, var); break; } } free;}

ok,完美。

因此UIWebview的代理方法管理U冠道L:

- webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (webView != _webView) { return YES; } NSURL *url = [request URL]; __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; if ([_base isCorrectProcotocolScheme:url]) { // 判断是不是sheme:wvjbscheme if ([_base isBridgeLoadedURL:url]) { // 判断是不是:wvjbscheme://__BRIDGE_LOADED__ [_base injectJavascriptFile]; // 注入js } else if ([_base isQueueMessageURL:url]) { // js注入后,触发新的url重定向 wvjbscheme://__WVJB_QUEUE_MESSAGE__ NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; // 获取js中的缓存队列数组字符串,清空队列 [_base flushMessageQueue:messageQueueString]; // 执行js中的缓存队列数组,全部执行完 } else { [_base logUnkownMessage:url]; // sheme:wvjbscheme 下其他的url 直接提示出错 } return NO; // 只是执行js注入,不会执行加载操作 } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { // 如果设置delegate,则调用代理方法 return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; } else { // 什么也不是正常执行 return YES; }}

此间对sheme:wvjbscheme的拍卖主借使三片段:

  • 注入JS
  • 拍卖消息
  • 别的错误Log提醒

对任何的sheme,如若设置delegate:

- setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate;

那么实施设置设置代理的方法。若无设置,那么WebView将不执行delegate的回调方法。就算你在UIViewController里面设置webView.delegate = self,也是不著见效的。

上面分析一下第一的四个支行。

3、通过U奇骏L的 scheme 和 host 决断 还会有 uniqueID (对应现实目的的block块)

Method 1 是JS注册八个艺术供OC调用,responseCallback(responseData)是将管理结果重返OC。

流入JS方法分支深入分析

此地经过isBridgeLoadedU酷路泽L方法是不是是注入的url,是的话实施注入JS代码injectJavascriptFile

- injectJavascriptFile { NSString *js = WebViewJavascriptBridge_js(); [self _evaluateJavascript:js]; if (self.startupMessageQueue) { // 一般是oc开始的注册handler方法 NSArray* queue = self.startupMessageQueue; self.startupMessageQueue = nil; for (id queuedMessage in queue) { [self _dispatchMessage:queuedMessage]; } }}
  • 那边会获得本地写好的JS代码,然后实践,顺便提一句_evaluateJavascript方法就是包装的stringByEvaluatingJavaScriptFromString。
  • 万一有缓存队列self.startupMessageQueue,实行里面整套的消息,一般是OC提前调用- registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler方法,提前注册JS调用OC的响应措施。

上面看下实行措施:_dispatchMessage:

- _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]; // 需要保证在主线程中执行js代码 if ([[NSThread currentThread] isMainThread]) { [self _evaluateJavascript:javascriptCommand]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ [self _evaluateJavascript:javascriptCommand]; }); }}
  • 首先将字典(WVJBMessage)类型数据转变到String
  • 做一些特殊字符的更改
  • 调用JS的WebViewJavascriptBridge._handleMessageFromObjC方法,将音讯传进去。

那么我们看一下JS的_handleMessageFromObjC()方法做了些什么

function _handleMessageFromObjC(messageJSON) { _dispatchMessageFromObjC(messageJSON); }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  { console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); } else { handler(message.data, responseCallback); } } } }
  • 率先做Json格式调换
  • 看清message是不是有responseId,有通过responseId推行本地的回调方法。
  • 从未有过responseId,回调handler方法,将数据,回调方法传回到,本地的callHandler是透过message.handlerName来标示的。要是有callbackId,正是本土援助回调,那么完毕一个无名氏函数接受回调。在触发responseCallback的时候,会有多个参数:

1)handlerName:调用的名称2)responseId:这里会把callbackId转换成responseId3)responseData:回调的数额音信

注意,responseId跟callbackId的区别:

  • callbackId: 用来标示回调的id,在触及回调的时候会把callbackId产生responseId
  • responseId: 用来施行回调的id

终极看下那些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; }
  • 其一办法实行音信的出殡,借使有回调方法的话,这里会变动多少个callbackId,然后存responseCallbacks中,message中新添callbackId的新闻。
  • 添加到sendMessageQueue队列中
  • 改变iframe的src触发Webview的回调

这里会触发UIWebview的回调,此时的url为:wvjbscheme://WVJB_QUEUE_MESSAGE恰巧走回调的第二个支行,上面剖判第叁个支行

Method 2 是在点击了贰个开关之后JS调用OC的措施,{'foo': 'bar'}是给OC的参数,response是OC管理后回去给JS的数码。

实施音讯分支剖判

else if ([_base isQueueMessageURL:url]) { // js注入后,触发新的url重定向 wvjbscheme://__WVJB_QUEUE_MESSAGE__ NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; // 获取js中的缓存队列数组字符串,清空队列 [_base flushMessageQueue:messageQueueString]; // 执行js中的缓存队列数组,全部执行完}

上边分两部分深入分析音讯的管理

1) 消息的取得这里会施行JS代码,通过webViewJavascriptFetchQueyCommand方法赢得JS的messageQueueString,也就上地点的sendMessageQueue里面包车型大巴音信。

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

JS中_fetchQueue()方法:

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

能够见到是一直把sendMessageQueue调换来string。

2) 音讯的施行

推行方法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) { WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; responseCallback(message[@"responseData"]); [self.responseCallbacks removeObjectForKey:responseId]; } else { WVJBResponseCallback responseCallback = NULL; NSString* callbackId = message[@"callbackId"]; if (callbackId) { // 如果接收到js的callbackId,初始化一个block,里面变callbackId为responseId 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); } }}

小编们来看此间思路跟JS里面是形似的。先将消息转变到JSON对象,然后便利全体的音信任务,假如有responseId,推行业地回调,并从self.responseCallbacks中移除推行过的回调方法。若无,通过message[@"handlerName"]实行self.messageHandlers中的回调,借使有callbackId,起初化三个responseCallback的block,并传播handler中,接收OC的再回调。

WebViewJavascriptBridge方法中提供格局:

typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);- callHandler:(NSString*)handlerName;- callHandler:(NSString*)handlerName data:data;- callHandler:(NSString*)handlerName data:data responseCallback:(WVJBResponseCallback)responseCallback;

那边需求提供JS里面包车型的士应和的handlerName,然后参数data,最终是响应的回调,提供JS回调的多少,一个responseCallback的Block,能够再回调JS。当然假若你无需回调,可以置为nil。

措施达成:

- callHandler:(NSString *)handlerName data:data responseCallback:(WVJBResponseCallback)responseCallback { [_base sendData:data responseCallback:responseCallback handlerName:handlerName];}// WebViewJavascriptBridgeBase中- 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];}

调用的是_base的sendData:方法,这里将参数放入字典message中,并把回调block放到self.responseCallbacks字典中。

  • data:存款和储蓄的数码
  • callbackId:标示block在self.responseCallbacks中的地方
  • handlerName:交互的名字

那样就一蹴即至了block回调在JS中不相配的标题,两端分别完结,用callbackId来标示,用responseId来施行。思路精确。

_queueMessage:实践那条message。

- _queueMessage:(WVJBMessage*)message { if (self.startupMessageQueue) { // startupMessageQueue:未在webview中注入js之前,缓存之前的交互消息message [self.startupMessageQueue addObject:message]; } else { [self _dispatchMessage:message]; }}

_dispatchMessage: 通过JS调用实施这条message,具体在眼下已经深入分析过了,这里不再赘述。

WebViewJavascriptBridge方法中提供情势:

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

措施完毕:

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

很简单,只是将hander放到_base(WebViewJavascriptBridgeBase的二个实例对象)的messageHandlers字典中,标示的key为handlerName。

那就结束了,然后等待是JS的调用触发,即:iframe.src设置触发UIWebView的Delegate回调。上边深入分析注入JS的时候解析过,这里不详细展开。

只要文中有怎么样错误,款待大家指正。

转发请表明出处:

注:JS中是足以不写;号的,那和swift一样

JS调用OC,OC将处理结果回调给JS:要想被JS调用,大家首先要注册贰个handler,和回调的            block,注册时候以键值对的样式积存这么些block,handler,当JS调用OC时调用webView:shouldStartLoadWithRequest:navigationType:那一个主意,依据JS传来的多少,找到在此之前封存的Block并且调用,同有的时候间新建三个亟待把管理结果回调给JS的Blcok,OC管理完毕果过后调用刚才成立的Block利用stringByEvaluatingJavaScriptFromString将管理结果再次来到给JS。

OC调用JS时与此类似。基于这些流程,我们来看WebViewJavaScriptBridge的贯彻进度。

原理

接下去我们来深入分析从页面加载到OC和JS互相调用的成套过程:

一、计划专门的职业

当加载HTML文件的时候调用[webView loadHTMLString:appHtml baseURL:baseURL];,那时会调用:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {if(webView != _webView) {returnYES; }NSURL*url = [request URL];    __strongWVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;if([_base isWebViewJavascriptBridgeURL:url]) {if([_base isBridgeLoadedURL:url]) {            [_base injectJavascriptFile];        }elseif([_base isQueueMessageURL:url]) {NSString*messageQueueString = [self_evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];            [_base flushMessageQueue:messageQueueString];        }else{            [_base logUnkownMessage:url];        }returnNO;    }elseif(strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {return[strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];    }else{returnYES;    }}


在那些主意中剖断UQashqaiL的品种,假使是WebViewJavascriptBridgeUQashqaiL那么就能够咬定是BridgeLoadedU凯雷德L,QueueMessageUCRUISERL照旧大惑不解的U福特ExplorerL,在第三次调用时是回到YES的,然后的UOdysseyL正是BridgeLoadedU安德拉L,大家在看它的决断规范[self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];Scheme是投机安装的https,那么BridgeLoaded(__bridge_loaded__)是哪些啊?大家看ExampleApp.html文件,开掘它的script标签中有诸有此类一段代码:

functionsetupWebViewJavascriptBridge(callback){if(window.WebViewJavascriptBridge) {returncallback(WebViewJavascriptBridge); }if(window.WVJBCallbacks) {returnwindow.WVJBCallbacks.push(callback); }window.WVJBCallbacks = [callback];varWVJBIframe =document.createElement('iframe');    WVJBIframe.style.display ='none';    WVJBIframe.src =');    setTimeout(function(){document.documentElement.removeChild(WVJBIframe) },0)}setupWebViewJavascriptBridge(function(bridge){varuniqueId =1functionlog(message, data){varlog =document.getElementById('log')varel =document.createElement('div')        el.className ='logLine'el.innerHTML = uniqueId '. ' message ':
' JSON.stringify(data)if(log.children.length) { log.insertBefore(el, log.children[0]) }else{ log.appendChild(el) }    }


在那边大家开掘了

[_base injectJavascriptFile]

流入一个JS文件,那么些JS文件的机要内容是(篇幅问题,有删减):

window.WebViewJavascriptBridge = {registerHandler: registerHandler,callHandler: callHandler,disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,_fetchQueue: _fetchQueue,_handleMessageFromObjC: _handleMessageFromObjC};varmessagingIframe;varsendMessageQueue = [];varmessageHandlers = {};varCUSTOM_PROTOCOL_SCHEME ='https';varQUEUE_HAS_MESSAGE ='__wvjb_queue_message__';varresponseCallbacks = {};varuniqueId =1;vardispatchMessagesWithTimeoutSafety =true;functionregisterHandler(handlerName, handler){    messageHandlers[handlerName] = handler;}functioncallHandler(handlerName, data, responseCallback){    _doSend();}functiondisableJavscriptAlertBoxSafetyTimeout(){    dispatchMessagesWithTimeoutSafety =false;}function_doSend(message, responseCallback){    sendMessageQueue.push(message);    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME '://' QUEUE_HAS_MESSAGE;}function_fetchQueue(){varmessageQueueString =JSON.stringify(sendMessageQueue);    sendMessageQueue = [];returnmessageQueueString;}function_dispatchMessageFromObjC(messageJSON){if(dispatchMessagesWithTimeoutSafety) {        setTimeout(_doDispatchMessageFromObjC);    }else{        _doDispatchMessageFromObjC();    }function_doDispatchMessageFromObjC(){        }    }}function_handleMessageFromObjC(messageJSON){    _dispatchMessageFromObjC(messageJSON);}messagingIframe =document.createElement('iframe');messagingIframe.style.display ='none';messagingIframe.src = CUSTOM_PROTOCOL_SCHEME '://' QUEUE_HAS_MESSAGE;document.documentElement.appendChild(messagingIframe);registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);setTimeout(_callWVJBCallbacks,0);function_callWVJBCallbacks(){varcallbacks =window.WVJBCallbacks;deletewindow.WVJBCallbacks;for(vari=0; i


上边大家来剖判下流入的JavaScript的剧情。

给window对象加多贰个特性WebViewJavascriptBridge(JS中能够直接给目的加多属性),这一个目标富含以下内容:

  1) registerHandler:注册调用方法

  2)callHandler:调用OC时的不二等秘书技

  3)disableJavscriptAlertBoxSafetyTimeout:超时时弹框是不是出示的标识

  4)_fetchQueue:获取Queue对象的主意

  5)_handleMessageFromObjC:管理OC调用的章程

2.概念了一名目多数的变量来积攒数据

messagingIframe:iframe标签,当我们的WebView加载它的时候,会调用个中的src,src便是调用央浼的U奥迪Q5L。

1)sendMessageQueue:message数组  2)messageHandlers:handler对象 *JS中{}表示对象*  3)CUSTOM_PROTOCOL_SCHEME:scheme标示  4)QUEUE_HAS_MESSAGE:有Message标识  5)responseCallbacks:回调对象  6)uniqueId:独一标示ID

进过类别一的分析,大家知道了动用WebViewJavaScriptBridge前急需做的预备职业,那么接下去,大家一同钻探OC和JS相互调用的有血有肉施行进程以及当中的要点。

二、 JS调用OC,然后OC将管理结果再次来到JS

1. OC第一注册JS将调用的方法

OC调用registerHandler:,那时将其调用音讯囤积在messageHandlers字典中以handlerName为Key,给JS管理结果的Block为Value(_base.messageHandlers[handlerName] = [handler copy]);

2. 在JS中调用被登记的艺术

JS调用

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

来调用上文OC注册的方法,那一个brige正是上文注入JS代码时候成立的,大家再它个中做了什么样。

functioncallHandler(handlerName, data, responseCallback){if(arguments.length ==2&&typeofdata =='function') {        responseCallback = data;        data =null;    }    _doSend({handlerName:handlerName,data:data }, responseCallback);}

此地判别了参数类型,要是传入的参数只有八个,况且第三个是function类型,那么就将第一个参数变为callBack,data置空,将handlerName和data转化成叁个对象的两本天性并传给_doSend()。

function_doSend(message, responseCallback){if(responseCallback) {varcallbackId ='cb_' (uniqueId ) '_' newDate().getTime();                responseCallbacks[callbackId] = responseCallback;                message['callbackId'] = callbackId;            }            sendMessageQueue.push(message);            messagingIframe.src = CUSTOM_PROTOCOL_SCHEME '://' QUEUE_HAS_MESSAGE;        }


这里的responseCallback是JS先调用OC然后OC调用JS时才会有个别,倘使这种情形,那么须求用独一的标识(callbackId),来将这几个responseCallback存款和储蓄在responseCallbacks中,并且给message增添callbackId那些本性。这些数值会在下一次OC调用JS的时候作为独一的Key被用到。软后将message归入:sendMessageQueue队列中,然后拼接src。

3. 在回掉方法中阻止相应的主意,然后调用block.

经过艺术步骤2,会调用下边包车型大巴回调方法

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType{}

在那么些主意中调用

NSString*messageQueueString = [self_evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];  [_base flushMessageQueue:messageQueueString];

先是取得JS中的messageQueue(步骤第22中学的sendMessageQueue),然后调用flushMessageQueue:方法:

idmessages = [self_deserializeMessageJSON:messageQueueString];for(WVJBMessage* messageinmessages) {if(![message isKindOfClass:[WVJBMessageclass]]) {NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [messageclass], message);continue;    }    [self_log:@"RCVD"json:message];/////////*********OC先调用了JS,JS再调用了OC*********///////////NSString* responseId = message[@"responseId"];if(responseId) {//调用从前存款和储蓄的BolckWVJBResponseCallback responseCallback = _responseCallbacks[responseId];        responseCallback(message[@"responseData"]);        [self.responseCallbacks removeObjectForKey:responseId];/////////*********JS先调用OC,OC再调用JS*********////////////// 这里是JS先调用OC的时候存款和储蓄的是 JS的回调函数}else{// JS先调用的OC,OC再调用JSWVJBResponseCallback responseCallback =NULL;NSString* callbackId = message[@"callbackId"];if(callbackId) {            responseCallback = ^(idresponseData) {if(responseData ==nil) {                    responseData = [NSNullnull];                }//JS调用OC时候的仓库储存(后续OC调用JS重临总括结果)WVJBMessage* msg = @{@"responseId":callbackId,@"responseData":responseData };                [self_queueMessage:msg];            };        }else{            responseCallback = ^(idignoreResponseData) {// Do nothing};        }                WVJBHandler handler =self.messageHandlers[message[@"handlerName"]];if(!handler) {NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);continue;        }//调用OC的Block,同时,如果OC调用responseCallback,则调用_queueMessage进行相应的拍卖handler(message[@"data"], responseCallback);    }}


此地先将重返的JSON字符串调换来对象,这里的字符串是调用

function_fetchQueue(){varmessageQueueString =JSON.stringify(sendMessageQueue);    sendMessageQueue = [];returnmessageQueueString;  }

获得的,这里将sendMessageQueue转为JSON,然后将其置空,这里为什么使用数组而不用对象来囤积吗?因为大概JS还平素不管理完毕就有三遍调用,要保管她们不遗弃使用了数组。然后决断数组中的Message对象是还是不是有responseId(JS调用OC第一遍时存储的),这里未有responseId所以走else:假使有callbackId(在JS中作为回调用的),定义responseCallback,这一个block就是OC将管理结果再次来到给JS时用到的block。若无callbackId表明,无需回调JS,今年responseCallback为空。最终调用步骤第11中学累积在messageHandlers对象中的block,况且将刚刚创立的responseCallback作为参数传入,以便OC将总结结果传递给JS。

4. OC将总计结果回到给JS

[_bridge registerHandler:@"testObjcCallback"handler:^(iddata, WVJBResponseCallback responseCallback) {NSLog(@"testObjcCallback called: %@", data);    responseCallback(@"response form oc's call back");  }];

在handler的尾声一步调用responseCallback()将管理结果回调给JS。这么些responseCallback()就是我们在步骤3中创设的responseCallback。大家再来看那几个block。看步骤3足以看出这么些其内部调用

[self_queueMessage:msg];  [self_dispatchMessage:message];

在_dispatchMessage内部实施:

NSString* javascriptCommand = [NSStringstringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];

接下来JS中的_handleMessageFromObjC就能吸纳到OC传过来管理结果。

function_doDispatchMessageFromObjC(){varmessage =JSON.parse(messageJSON);varmessageHandler;varresponseCallback;if(message.responseId) {            responseCallback = responseCallbacks[message.responseId];if(!responseCallback) {return;            }            responseCallback(message.responseData);deleteresponseCallbacks[message.responseId];        }else{// OC先调用JS是用到}    }

本条时候我们看出了手续三中的responseId的效力了,那时候responseId就标识了是OC将管理结果传递给JS并没有须要JS再调用OC了,那时只调用responseCallback(message.responseData);将数据传给JS。

如此大家就变成了JS调用OC,然后OC将结果回调给JS的整个进度。

三、OC调用JS,然后JS将管理结果重临给OC

1. JS注册相应的办法供回调

同OC注册方式时候一样,JS也是用多个messageHandlers对象来囤积

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

2. OC调用JS时存款和储蓄调用音讯

- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {NSMutableDictionary* message = [NSMutableDictionarydictionary];if(data) {            message[@"data"] = data;        }if(responseCallback) {NSString* callbackId = [NSStringstringWithFormat:@"objc_cb_%ld", _uniqueId];self.responseCallbacks[callbackId] = [responseCallbackcopy];            message[@"callbackId"] = callbackId;        }if(handlerName) {            message[@"handlerName"] = handlerName;        }        [self_queueMessage:message];    }

此处运用message字典来囤积参数,方法名,使用responseCallbacks来存储JS管理完事后,需求回调的Block(这里为了保险数11次调用不会覆盖从前的调用,使用了独一的callbackId)。

同上文所述,最后会调用

- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {return[_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];    }

3. JS调用_dispatchMessageFromObjC

这时message没有responseId,会走else,

if(message.callbackId) {varcallbackResponseId = message.callbackId;                    responseCallback =function(responseData){                        _doSend({handlerName:message.handlerName,responseId:callbackResponseId,responseData:responseData });                    };                }varhandler = messageHandlers[message.handlerName];if(!handler) {console.log("WebViewJavascriptBridge: WALacrosseNING: no handler for message from ObjC:", message);                }else{                    handler(message.data, responseCallback);                } 这里定义了急需给OC传递结果的`responseCallback`,抽出之前注册的`handler`:`messageHandlers[message.handlerName]`,然后调用这些`handler`,并将那些`responseCallback`作为参数传进去,`handler(message.data, responseCallback);`

4. JS将结果回传给OC

在步骤三中调用handler:function(data, responseCallback){            log('ObjC called testJavascriptHandler with', data)varresponseData = {'Javascript Says':'Right back atcha!'}            log('JS responding with', responseData)            responseCallback(responseData)        } 在这个`handler`的末尾调用步骤三种的`responseCallback`(传入的只有多少尚未回调),依据步骤三能够看出来其会调用`_doSend`格局。该方法中出于并未有传进去回调,所以不会给message对象增多`callbackId`,只调用        sendMessageQueue.push(message);          messagingIframe.src = CUSTOM_PROTOCOL_SCHEME '://' QUEUE_HAS_MESSAGE;

这是出于含有responseId(在步骤三中的_doSend调用时设置),所以只会收取此前存款和储蓄的block,并且将结果回传给OC:

//调用以前存款和储蓄的BolckWVJBResponseCallback responseCallback = _responseCallbacks[responseId];        responseCallback(message[@"responseData"]);        [self.responseCallbacks removeObjectForKey:responseId];

从那之后,OC和JS交互的具备逻辑已介绍实现(WKWebView完结情势同样),总括下二种景况的回调,其促成格局及其相似,正如小说初步的计算。

本文由pc28.am发布于计算机编程,转载请注明出处:相互调用的框架分析,WebViewJavaScriptBridge源码剖析

上一篇:获取芝麻信用分最全档案,支付宝集成流程 下一篇:没有了
猜你喜欢
热门排行
精彩图文