WebViewJavascriptBridge浅析
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环境的初始化
- WebViewJavascriptBridge *bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView ]; //初始化调用
- //初始化一个OC环境的桥WKWebViewJavascriptBridge并且初始化
- + (instancetype)bridgeForWebView:(WKWebView*)webView {
- WKWebViewJavascriptBridge* bridge = [[self alloc] init];
- [bridge _setupInstance:webView];
- [bridge reset];
- return bridge;
- }
- //初始化
- - (void) _setupInstance:(WKWebView*)webView {
- _webView = webView;
- _webView.navigationDelegate = self;
- _base = [[WebViewJavascriptBridgeBase alloc] init];
- _base.delegate = self;
- }
- - (id)init {
- if (self = [super init]) {
- //用于保存OC环境注册的方法,key是方法名,value是这个方法对应的回调block
- self.messageHandlers = [NSMutableDictionary dictionary];
- //用于保存交互过程中需要发送给javascirpt环境的消息
- self.startupMessageQueue = [NSMutableArray array];
- //用于保存OC和javascript环境相互调用的回调模块。通过_uniqueId加上时间戳来确定每个调用的回调
- self.responseCallbacks = [NSMutableDictionary dictionary];
- _uniqueId = ;
- }
- return self;
- }
【结论】:所有与Javascript之间交互的信息都存储在messageHandlers和responseCallbacks中。这两个属性记录了OC环境与Javascript交互的信息。
1.2OC环境注册方法
注册一个OC方法给J
avascript调用,并且把它的回调实现保存在messageHandlers
中。具体代码如下:
- //注册OC方法,以供Javascript调用
- [self.bridge registerHandler:@"testClientCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
- NSLog(@"Javascript传递数据: %@", data);
- responseCallback(@"OC发给JS的返回值");
- }];
- - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
- _base.messageHandlers[handlerName] = [handler copy];
- }
1.3Javascript初始化&注册方法
这个我们先来看一下WebViewJavascriptBridge上的示例ExampleAPP.html:
- function setupWebViewJavascriptBridge(callback) {
- //第一次调用这个方法的时候,为false
- if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
- //第一次调用的时候,为false
- if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
- //把callback对象赋值给对象
- window.WVJBCallbacks = [callback];
- //加载WebViewJavascriptBridge_JS中的代码
- 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)
- }
- //驱动所有hander的初始化
- setupWebViewJavascriptBridge(function(bridge) {
- //把WEB中要注册的方法注册到bridge里面
- bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
- //OC中传过来的数据
- log('ObjC called testJavascriptHandler with', data);
- //JS返回数据
- var responseData = { 'Javascript Says':'Right back atcha!' };
- responseCallback(responseData);
- })
- })
【说明】:调用setupWebViewJavascriptBridge函数,并且这个函数传入的参数也是一个函数。参数函数中有在Javascript环境中注册的setupWebViewJavascriptBridge的实现过程中,我们可以发现,如果不是第一次初始化,会通过window.WVJBCallbacks两个判断返回。iframe可以理解为webview中的窗口,当我们改变iframe的src属性的时候,相当于我们浏览器实现了链接的跳转。比如从www.google.com。下面这段代码的目的就是实现一个到https://__bridge_loaded__的跳转。从而达到初始化Javascript环境的bridge的作用。
- //加载WebViewJavascriptBridge_JS中的代码
- 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)
我们知道,只要webview有跳转,就会调用webview的代理方法,我们重点看下面这个代理方法。
- - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
- if (webView != _webView) { return; }
- NSURL *url = navigationAction.request.URL;
- __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
- //如果是WebViewJavascriptBridge发送或者接收的消息,则特殊处理。否则按照正常流程处理。
- if ([_base isWebViewJavascriptBridgeURL:url]) {
- //1第一次注入JS代码
- if ([_base isBridgeLoadedURL:url]) {
- [_base injectJavascriptFile];
- } else if ([_base isQueueMessageURL:url]) {//处理WEB发过来的消息
- [self WKFlushMessageQueue];
- } else {
- [_base logUnkownMessage:url];
- }
- decisionHandler(WKNavigationActionPolicyCancel);
- return;
- }
- //webview的正常代理执行流程
- if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
- [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
- } else {
- decisionHandler(WKNavigationActionPolicyAllow);
- }
- }
上面的方法中,首先通过isWebViewJavascriptBridgeURL 来判断是普通的跳转还是webViewJavascriptBridge跳转。如果是__bridge_loaded__ 表示是初始化Javascript环境的消息;如果是__WVJB_QUEUE_MESSAGE__ 表示是发送Javascript消息。我们继续看几个核心方法的实现:
- #define kOldProtocolScheme @"wvjbscheme"
- #define kNewProtocolScheme @"https"
- #define kQueueHasMessage @"__wvjb_queue_message__"
- #define kBridgeLoaded @"__bridge_loaded__"
- //是否是WebViewJavascriptBridge框架相关的链接
- - (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url {
- if (![self isSchemeMatch:url]) {
- return NO;
- }
- return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url];
- }
- //是否是WebViewJavascriptBridge发送或者接收的消息
- - (BOOL)isSchemeMatch:(NSURL*)url {
- NSString* scheme = url.scheme.lowercaseString;
- return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme];
- }
- //是WebViewJavascriptBridge发送的消息还是WebViewJavascriptBridge的初始化消息
- - (BOOL)isQueueMessageURL:(NSURL*)url {
- NSString* host = url.host.lowercaseString;
- return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage];
- }
- //是否是https://__bridge_loaded__这种初始化加载消息
- - (BOOL)isBridgeLoadedURL:(NSURL*)url {
- NSString* host = url.host.lowercaseString;
- return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];
- }
接下来调用injectJavascriptFile方法,将WebViewJavascriptBridge_JS中的方法注入到webview中并且执行,从而达到初始化Javascript环境的brige的作用。
- - (void)injectJavascriptFile {
- NSString *js = WebViewJavascriptBridge_js();
- //把javascript代码注入webview中执行
- [self _evaluateJavascript:js];
- //javascript环境初始化完成以后,如果有startupMessageQueue消息,则立即发送消息
- if (self.startupMessageQueue) {
- NSArray* queue = self.startupMessageQueue;
- self.startupMessageQueue = nil;
- for (id queuedMessage in queue) {
- [self _dispatchMessage:queuedMessage];
- }
- }
- }
- //把javascript代码写入webview
- - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
- [_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
- return NULL;
- }
那么WebViewJavascriptBridge_JS到底是怎么实现的呢?
- NSString * WebViewJavascriptBridge_js() {
- #define __wvjb_js_func__(x) #x
- // BEGIN preprocessorJSCode
- static NSString * preprocessorJSCode = @__wvjb_js_func__(
- ;(function() {
- //如果已经初始化了,则返回
- if (window.WebViewJavascriptBridge) {
- return;
- }
- if (!window.onerror) {
- window.onerror = function(msg, url, line) {
- console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line);
- }
- }
- //初始化Bridge对象,OC可以通过WebViewJavascriptBridge来调用JS里面的各种方法
- window.WebViewJavascriptBridge = {
- registerHandler: registerHandler, //JS中注册方法
- callHandler: callHandler, //JS中调用OC中的方法
- disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
- _fetchQueue: _fetchQueue, //把消息转换成JSON串
- _handleMessageFromObjC: _handleMessageFromObjC //OC调用JS的入口方法
- };
- var messagingIframe;
- //用于存储消息列表
- var sendMessageQueue = [];
- //用于存储消息
- var messageHandlers = {};
- //通过下面两个协议组合来确定是否是特定的消息,然后拦击
- var CUSTOM_PROTOCOL_SCHEME = 'https';
- var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
- //oc调用js的回调
- var responseCallbacks = {};
- //消息对应的id
- var uniqueId = 1;
- //是否设置消息超时
- var dispatchMessagesWithTimeoutSafety = true;
- //web端注册一个消息方法
- function registerHandler(handlerName, handler) {
- messageHandlers[handlerName] = handler;
- }
- //web端调用一个OC注册的消息
- function callHandler(handlerName, data, responseCallback) {
- if (arguments.length == 2 && typeof data == 'function') {
- responseCallback = data;
- data = null;
- }
- _doSend({ handlerName:handlerName, data:data }, responseCallback);
- }
- function disableJavscriptAlertBoxSafetyTimeout() {
- dispatchMessagesWithTimeoutSafety = false;
- }
- //把消息从JS发送到OC,执行具体的发送操作
- function _doSend(message, responseCallback) {
- if (responseCallback) {
- var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
- //存储消息的回调ID
- responseCallbacks[callbackId] = responseCallback;
- //把消息对应的回调ID和消息一起发送,以供消息返回以后使用
- message['callbackId'] = callbackId;
- }
- //把消息放入消息列表
- sendMessageQueue.push(message);
- //下面这句话会出发JS对OC的调用,让webview执行跳转操作,从而可以在
- //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
- //中拦截到JS发给OC的消息
- messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
- }
- //把消息转换成JSON字符串返回
- function _fetchQueue() {
- var messageQueueString = JSON.stringify(sendMessageQueue);
- sendMessageQueue = [];
- return messageQueueString;
- }
- //处理从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) {
- var callbackResponseId = message.callbackId;
- responseCallback = function(responseData) {
- _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
- };
- }
- //获取JS注册的函数
- var handler = messageHandlers[message.handlerName];
- if (!handler) {
- console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
- } else { //调用JS中的对应函数处理
- handler(message.data, responseCallback);
- }
- }
- }
- }
- //OC调用JS的入口方法
- 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);
- //注册_disableJavascriptAlertBoxSafetyTimeout方法,让OC可以关闭回调超时,默认是开启的
- registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
- //执行_callWVJBCallbacks方法
- setTimeout(_callWVJBCallbacks, 0);
- //初始化WEB中注册的方法。这个方法会把WEB中的hander注册到bridge中
- //下面的代码其实就是执行WEB中的callback函数
- function _callWVJBCallbacks() {
- var callbacks = window.WVJBCallbacks;
- delete window.WVJBCallbacks;
- for (var i=0; i<callbacks.length; i++) {
- callbacks[i](WebViewJavascriptBridge);
- }
- }
- })();
- ); // END preprocessorJSCode
- #undef __wvjb_js_func__
- return preprocessorJSCode;
- };
从上面可以看到,整个类就是一个立即执行的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代理方法的调用。
- messagingIframe = document.createElement('iframe');
- messagingIframe.style.display = 'none';
- messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
- document.documentElement.appendChild(messagingIframe);
上面的src就是https://__wvjb_queue_message__/。这个是javascript发送的OC的第一条消息,目的和上面OC环境的startupMessageQueue一样,就是在javascript环境初始化完成以后,把javascript要发送给OC的消息立即发送出去。
1.4OC发消息给Javascript
- - (void)p_callJSHandler:(id)sender {
- id data = @{ @"OC调用JS方法": @"OC调用JS方法的参数" };
- [self.bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {
- NSLog(@"JS 响应数据: %@", response);
- }];
- }
- /**
- OC调用JS方法
- @param handlerName JS中提供的方法名称
- @param data 参数
- @param responseCallback 回调block
- */
- - (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
- [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
- }
- - (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
- //所有信息存入的字典
- NSMutableDictionary* message = [NSMutableDictionary dictionary];
- if (data) {
- 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];
- }
- //把OC消息序列化、并且转化为JS环境的格式,然后在主线程中调用_evaluateJavascript
- - (void)_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];
- });
- }
- }
打印javascriptCommand,结果如下:
- WebViewJavascriptBridge._handleMessageFromObjC('{\"callbackId\":\"objc_cb_1\",\"data\":{\"OC调用JS方法\":\"OC调用JS方法的参数\"},\"handlerName\":\"testJavascriptHandler\"}');
实际上就是执行JS环境中的_handleMessageFromObjC方法:
- //处理从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) {
- var callbackResponseId = message.callbackId;
- responseCallback = function(responseData) {
- _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
- };
- }
- //获取JS注册的函数
- var handler = messageHandlers[message.handlerName];
- if (!handler) {
- console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
- } else { //调用JS中的对应函数处理
- handler(message.data, responseCallback);
- }
- }
- }
- }
- //把消息从JS发送到OC,执行具体的发送操作
- function _doSend(message, responseCallback) {
- if (responseCallback) {
- var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
- //存储消息的回调ID
- responseCallbacks[callbackId] = responseCallback;
- //把消息对应的回调ID和消息一起发送,以供消息返回以后使用
- message['callbackId'] = callbackId;
- }
- //把消息放入消息列表
- sendMessageQueue.push(message);
- //下面这句话会出发JS对OC的调用,让webview执行跳转操作,从而可以在
- //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
- //中拦截到JS发给OC的消息
- messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
- }
最重要的是最后面的通过改变iframe的messagingIframe.src
,只有这样才能触发webview的代理方法webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
从而在OC中处理javascript环境触发过来的回调。
- //第一次注入JS代码
- if ([_base isBridgeLoadedURL:url]) {
- [_base injectJavascriptFile];
- } else if ([_base isQueueMessageURL:url]) {//处理WEB发过来的消息
- [self WKFlushMessageQueue];
- } else {
- [_base logUnkownMessage:url];
- }
- decisionHandler(WKNavigationActionPolicyCancel);
这里会走[self WKFlushMessageQueue];
方法:
- //把消息或者WEB回调发送到OC
- - (void)WKFlushMessageQueue {
- [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
- if (error != nil) {
- NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
- }
- [_base flushMessageQueue:result];
- }];
- }
最后执行flushMessageQueue,通过Javascript传过来的responseId获取对应的
WVJBResponseCallback
,执行这个block。到这里从OC发送消息到Javascript并且Javascript返回消息给OC的流程就完成了。
1.5JS发消息给OC
- bridge.callHandler('testObjcHandler', {'foo': 'bar'}, function(response) {
- log('JS got response', response)-->
- })
- //web端调用一个OC注册的消息
- function callHandler(handlerName, data, responseCallback) {
- if (arguments.length == && typeof data == 'function') {
- responseCallback = data;
- data = null;
- }
- _doSend({ handlerName: handlerName, data: data }, responseCallback);
- }
- //JS调用OC方法,执行具体的发送操作
- function _doSend(message, responseCallback) {
- if (responseCallback) {
- var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
- //存储消息的回调ID
- responseCallbacks[callbackId] = responseCallback;
- //把消息对应的回调ID和消息一起发送,以供消息返回以后使用
- message['callbackId'] = callbackId;
- }
- //把消息放入消息列表
- sendMessageQueue.push(message);
- //下面这句话会发起JS对OC的调用,让webview执行跳转操作,从而可以在
- //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
- //中拦截到JS发给OC的消息
- messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
- }
具体执行和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浅析的更多相关文章
- 浅析hybrid模式下地支付宝钱包和微信
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VuY2hhbzEyNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- SQL Server on Linux 理由浅析
SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 高性能IO模型浅析
高性能IO模型浅析 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking ...
- netty5 HTTP协议栈浅析与实践
一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...
- Android混合开发之WebViewJavascriptBridge实现JS与java安全交互
前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...
- Jvm 内存浅析 及 GC个人学习总结
从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...
- 从源码浅析MVC的MvcRouteHandler、MvcHandler和MvcHttpHandler
熟悉WebForm开发的朋友一定都知道,Page类必须实现一个接口,就是IHttpHandler.HttpHandler是一个HTTP请求的真正处理中心,在HttpHandler容器中,ASP.NET ...
- 【深入浅出jQuery】源码浅析2--奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
随机推荐
- Java 身份证判断性别获取年龄
import com.alibaba.fastjson.JSON; import org.junit.Test; import java.text.SimpleDateFormat; import j ...
- SpringMVC 请求全过程漫谈
SpringMVC 请求全过程漫谈 SpringMVC 跟其他的mvc框架一样,如 struts,webwork, 本质上都是 将一个 http 请求(request)进行各种处理, 然后返回resp ...
- Android开发中常见的设计模式(四)——策略模式
策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变换. 假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等 ...
- vscode的环境变量code
vscode的安装路径 本质:vscode的安装路径/Applications/Visual Studio Code.app/Contents/Resources/app/bin 下面有code可执行 ...
- JS 正则表达式基本语法(精粹)
1.正则表达式基本语法 两个特殊的符号'^'和'$'.他们的作用是分别指出一个字符串的开始和结束. 例子如下: "^The":表示所有以"The"开始的字符串( ...
- c++冒号作用
转自http://www.360doc.com/content/13/0605/11/3373961_290615318.shtml 1.冒号(:)用法 (1)表示机构内位域的定义(即该变量占几个bi ...
- 把Gitlab迁移到Docker容器里
把Gitlab迁移到Docker容器里 Apr 9, 2015. | By: 任怀林 公司的gitlab一直是运行在ovm的虚拟机里的,版本还是6.7.5.版本有点老了,最近在研究docker,于是想 ...
- CSS表单3 光标样式 (每个位置鼠标放上去的样式不同)
<!DOCTYPE html> <html> <head> <title>单选按钮对齐</title> ...
- python21期day01笔记总结
2019.3.27 S21 day01笔记总结 一.计算机基础知识 1.计算机组成 用户 应用软件程序开发——用到了两个方面: 1语法 : 2解释器.编译器.虚拟机: 操作系统的开发 硬件组成 2.操 ...
- animation渐进实现点点点等待效果
<style> @keyframes dot { 0% { width: 0; } 33% { width: .2em; } 66% { width: .5e ...