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 = 0;
    }
    return self;
}

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

1.2OC环境注册方法

注册一个OC方法给Javascript调用,并且把它的回调实现保存在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 == 2 && 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的功能。

原文地址:https://www.cnblogs.com/LeeGof/p/8143408.html

时间: 2024-10-30 17:52:21

WebViewJavascriptBridge浅析的相关文章

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

开篇 近期一直在为插件式开发而不断探询,对于android端已经有一套插件式开发的框架,有通过java的反射机制实现的plugin,但目前我还没有找到一套完整的开源的框架,后期继续寻找.而对于iOS端,由于object-c没有java的反射机制,于是就想到了动态加载的方式,而网友们发现苹果公司禁止开发者使用动态库,而只允许使用静态库,所以使用加载动态库的方式目前是行不通了. 那现在就剩下两条路,就是采用webapp开发和hybrid模式,对于webapp,现在也是许多人探索的道路了,这种方式的好

Python之encode与decode浅析

 Python之encode与decode浅析 在 python 源代码文件中,如果你有用到非ASCII字符,则需要在文件头部进行字符编码的声明,声明如下: # code: UTF-8 因为python 只检查 #.coding 和编码字符串,为了美观等原因可以如下写法: #-*-coding:utf-8-*- 常见编码介绍: GB2312编码:适用于汉字处理.汉字通信等系统之间的信息交换. GBK编码:是汉字编码标准之一,是在 GB2312-80 标准基础上的内码扩展规范,使用了双字节编码.

浅析PHP的开源产品二次开发的基本要求

浅析PHP的开源产品二次开发的基本要求 第一, 基本要求:HTML(必须要非常熟悉),PHP(能看懂代码,能写一些小系统,如:留言板,小型CMS),Mysql(至少会一种数据库),Javascript(能看懂,能改现成的一些代码),Div+Css(能进行界面的调整,明白CSS是怎么使用的) 第二, 熟悉开源产品的使用,比如 Dedecms,你要知道怎么登录,怎么新建栏目,怎么添加文章,模板标签的使用方法,模型的概念和使用方法等等一些功能 第三, 要熟悉这个开源产品的数据库结构,还要理解里面核心文

word-break|overflow-wrap|word-wrap——CSS英文断句浅析

---恢复内容开始--- word-break|overflow-wrap|word-wrap--CSS英文断句浅析 一 问题引入 今天在再次学习 overflow 属性的时候,查看效果时,看到如下结果,内容在 div 中国换行了,可是两个 P 元素的内容并没有换行,搜索一番没有找到系统的答案,截图到群里请教大神,才知道是英文断句的问题,但是还是不太明白.之前没有遇到这种情况,为了彻底搞清楚,英文断句,又开始学习英文断句到底是怎么回事. 二 换行 每种语言里都有换行,就中文而言,我们最小语言单位

浅析vanish

浅析 VANISH --一种cache 第一部分:理解vanish的准备工作 1.对CDN的小剖析 CDN  content  delivery  network  内容分发(推送)网络,是在现有的Internet中增加一层新的网络架构,将网络内容发布到最接近用户的网络边缘(边缘服务器),使用户最近取得所需内容,解决网络拥挤状态,提高用户访问网站的速度. CDN网络架构主要有两部分组成,中心和边缘两部分,中心指CDN网管中心和DNS重定向解析中心,负责全局负载均衡.边缘主要指异地节点,CDN分发

健康,home? [java的内存浅析]

健康,home? [java的内存浅析] 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 乐观上上,how can other kno u,u r yourself!I must be strong and carry on. -泥沙砖瓦浆木匠 一.闲谈下 201407月记着那时候身体垮了下来,呵呵.想说,对自己的说,也是对大家的负责吧.那时候胸疼胸闷,然后几乎累垮了,我还坚持了一星期,那一星期真的迷迷糊糊.完全不能

Mysql查询优化器浅析

--Mysql查询优化器浅析 -----------------------------2014/06/11 1 定义 Mysql查询优化器的工作是为查询语句选择合适的执行路径.查询优化器的代码一般是经常变动的,这和存储引擎不太一样.因此,需要理解最新版本的查询优化器是如何组织的,请参考相应的源代码.整体而言,优化器有很多相同性,对mysql一个版本的优化器做到整体掌握,理解起mysql新版本以及其他数据库的优化器都是类似的. 优化器会对查询语句进行转化,转化等价的查询语句.举个例子,优化器会将

Volley框架源码浅析(一)

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Voll

C语言中文件打开模式(r/w/a/r+/w+/a+/rb/wb/ab/rb+/wb+/ab+)浅析

C语言文件打开模式浅析 在C语言的文件操作语法中,打开文件文件有以下12种模式,如下图: 打开模式  只可以读   只可以写  读写兼备 文本模式 r w a r+ w+ a+ 二进制模式 rb wb ab  rb+ (r+b)   wb+ (w+b)   ab+ (a+b)  其中,二进制模式与文本模式操作相似,只不过是以二进制流的形式读写而已,下面以文本模式为例分析: 1."r" 模式: 1.1 打开文件进行“只读”操作,即只能从文件读取内容. 1.2 若欲操作的文件不存在,则打开