美文网首页
WebViewJavascriptBridge建立bridge、

WebViewJavascriptBridge建立bridge、

作者: suoluomen | 来源:发表于2019-12-23 10:29 被阅读0次

    1.提出问题

    • 诉求
      在一个Web界面中,需要OC代码直接调用Web中的js函数,同时也需要Web中的JS代码直接调用OC中方法。
      iOS提供可实现原理:通过webView中的方法拦截Web界面分析request值的内容后,或根据约定的字段调用移动端对应的代码执行,或编写JavaScript代码操作Web界面。
    UIWebView
    // 拦截Web的request
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        if (webView != _webView) { return YES; }
        NSURL *url = [request URL];
        ``` 其他操作```
       }
    // 操作Web界面方法
    [self.webView stringByEvaluatingJavaScriptFromString:javascriptSting];
    -----------------------------------------------------------------------------------------------------------
    WKWebView
    // 拦截Web的request
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        if (webView != _webView) { return; }
        NSURL *url = navigationAction.request.URL;
      }
    // 操作Web界面方法
    [self.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
    

    2.使用WebViewJavascriptBridge解决

    WebViewJavascriptBridge:An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews, UIWebViews & WebViews.

    WebViewJavascriptBridge就是通过上面👆的原理封装而成,为我们建造了一座桥(bridge)。桥的本质:其实就类似于在OC端和Web端各创建一个名字叫bridge的通讯兵(OC: WKWebViewJavascriptBridge/WebViewJavascriptBridge,Web: WebViewJavascriptBridge_JS),这两个通讯兵有一套相同的处理信息机制,然后根据 【对方信息处理后的结果】找到自己端对应的事件后,执行该事件,那么这个桥就是通讯兵互相传递消息的行为。

    下图为WebViewJavascriptBridge的代码结构:
    - WebViewJavascriptBridge_JS (负责存储Web端的事件和回调,以及向OC端接受和发送json串指令)
    - WebViewJavascriptBridgeBase (负责存储OC端的事件和回调,以及向Web端接受和发送json串指令)
    - WKWebViewJavascriptBridge、WebViewJavascriptBridge (是WebViewJavascriptBridgeBase的接口层以供针对不同容器使用(UIWebView和WKWebView),同时也负责拦截Web端request,供WebViewJavascriptBridgeBase使用)

    WebViewJavascriptBridge代码结构.png

    3. Web端Bridge的实现过程

    OC端创建bridge很简单只需要WKWebViewJavascriptBridge/WebViewJavascriptBridge创建一个实例对象,调用相关方法即可,具体可见下面代码。而Web端创建bridge需要经历一些事情,以下内容便是Web端bridge创建过程。

    • 准备建立bridge :
      Web端,将下面👇function代码粘贴在Web端的JS代码中(添加这段代码是:由前端人员开发H5界面时在JS代码中添加的),以备调用去建立bridge。

      OC端,初始化一个WebView和WebViewJavascriptBridge对象(WKWebView和WKWebViewJavascriptBridge对象),并将WebView的代理交给WebViewJavascriptBridge对象实现。

    *JS创建bridge
    function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        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)
    }
    
    *OC创建bridge
    - (void)viewWillAppear:(BOOL)animated {
        if (_bridge) { return; }
        UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
        _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
        [_bridge setWebViewDelegate:self];
        [self.view addSubview:webView];
    
        // 注册OC端的事件,以备JS端调用
        [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"testObjcCallback called: %@", data);
            responseCallback(@"Response from testObjcCallback");
        }];
       
    }
    

    *Web端建立bridge是在OC加载Web界面时,Web中JS代码调用function setupWebViewJavascriptBridge(callback) {...}建立bridge,但是真正创建Web端的bridge是在WebViewJavascriptBridge_JS.m文件中的JS代码中。那么怎么才能让WebViewJavascriptBridge_JS.m文件中的JS代码运行呢?接着往下看

    setupWebViewJavascriptBridge(function(bridge) {
        bridge.registerHandler('JS Echo', function(data, responseCallback) {
            console.log("JS Echo called with:", data)
            responseCallback(data)
        })
        bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
            console.log("JS received response:", responseData)
        })
    })
    

    调用setupWebViewJavascriptBridge方法之后发生了什么呢?注意下面代码注释。

    // OC端加载Web时,Web端调用setupWebViewJavascriptBridge方法
    setupWebViewJavascriptBridge(
            // function(bridge){...} 就是setupWebViewJavascriptBridge方法参数callback。
            function(bridge) {
               // Web端通过桥bridge注册OC端调用JS端的相关事件,以备OC端调用,方法registerHandler(handleName , handle )声明实现在文件WebViewJavascriptBridge_JS.m中。
               bridge.registerHandler(
                               // handleName  
                               'JS Echo', 
                               // handle
                               function(data, responseCallback) {
                                    console.log("JS Echo called with:", data)
                                    responseCallback(data) 
                                 }
                       )
             }
    )
    
    // 调用方法,将参数callback传进来
    function setupWebViewJavascriptBridge(callback) {
        //判断window中是否有WebViewJavascriptBridge属性,属性WebViewJavascriptBridge其实就是桥(bridge),这个属性的声明和初始化在文件WebViewJavascriptBridge_JS.m中。
        //如果bridge存在,则直接执行callback,在callback中注册OC端调用JS端的相关事件。如果不存在则略过。
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        //判断WVJBCallbacks是否存在,该属性在下行有声明和实现,主要负责临时存储callback,等待bridge建立成功后,再将callback拿出来运行,注册OC端调用JS端的相关事件。
        // 如果WVJBCallbacks存在,则将callback存起来,如果不存在则继续执行,初始化WVJBCallbacks属性并将callback存起来。
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        // 初始化WVJBCallbacks属性并将callback存起来。
        window.WVJBCallbacks = [callback];
        // 初始化一个WVJBIframe元素,设置其隐藏不显示,设定其链接url为 'https://__bridge_loaded__',然后添加到Web后执行了src的请求,随后OC的WebView的代理方法会拦截到request,对url进行比对,如果url为https://__bridge_loaded__则会将WebViewJavascriptBridge_JS.m中的JS代码注入Web界面并执行,创建出bridge(具体实现过程在OC端实现,稍后再说)
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
    // 当代码执行至此,bridge建立,callback调用已经完成,这些操作均在文件WebViewJavascriptBridge_JS.m中的JS代码中执行。那下面一句操作,便是移除WVJBIframe元素,因为它的使命已经完成(使命:发起一个请求,能使OC端拦截url)。
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
    
    • Web端JS代码执行到:document.documentElement.appendChild(WVJBIframe);后,OC的WebView的代理方法会拦截到request,对url进行比对,如果url为 https://__bridge_loaded__ 则会将WebViewJavascriptBridge_JS.m中的JS代码注入并执行。
    // WebView的代理方法,WebViewJavascriptBridge.m中的代码,代码中的_base: WebViewJavascriptBridgeBased实例对象
    - (BOOL)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;
        // 判断截获的URL是否是桥接所用到的url(桥接共使用了两个url,1. https://__bridge_loaded__仅用于建立桥,2.https://__wvjb_queue_message__仅用于Web呼唤OC端处理事务使用)
        if ([_base isWebViewJavascriptBridgeURL:url]) {
            
            if ([_base isBridgeLoadedURL:url]) { // 判断url是否为https://__bridge_loaded__
                // 建立桥,OC端将WebViewJavascriptBridge_JS.m中的JS代码注入Web界面并执行
                [_base injectJavascriptFile];
            } else if ([_base isQueueMessageURL:url]) { 判断url是否为https://__wvjb_queue_message__,只有在桥建立完成后才会调用这个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;
        }
    }
    
    • 没错,就在这里我们调用到了WebViewJavascriptBridge_JS.m中的JS代码。代码执行到[_base injectJavascriptFile];时,说明Web端通过https://__bridge_loaded__呼唤到了OC端,并通过拦截requst,告诉OC端要建立桥。具体见 injectJavascriptFile 函数代码
    - (void)injectJavascriptFile {
        // 获取WebViewJavascriptBridge_JS.m文件中的JS代码
        NSString *js = WebViewJavascriptBridge_js();
       // 以代理的方式,回调回去,让WebView来执行 [self.webView stringByEvaluatingJavaScriptFromString:javascriptSting];
        [self _evaluateJavascript:js];
        if (self.startupMessageQueue) {
            NSArray* queue = self.startupMessageQueue;
            self.startupMessageQueue = nil;
            for (id queuedMessage in queue) {
                [self _dispatchMessage:queuedMessage];
            }
        }
    }
    
    • 那么接下来看一下JS代码中做了什么
    // 初始化了bridge,{···}中是bridge相关的方法
    window.WebViewJavascriptBridge = {
            registerHandler: registerHandler,
            callHandler: callHandler,
            disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
            _fetchQueue: _fetchQueue,
            _handleMessageFromObjC: _handleMessageFromObjC
        };
        // 初始化一个messagingIframe,加载url:https://__wvjb_queue_message__,OC端拦截到url,又做了一些列操作,稍后再说
        messagingIframe = document.createElement('iframe');
        messagingIframe.style.display = 'none';
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
        document.documentElement.appendChild(messagingIframe);
    
        registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
        //待上面操作,全部执行完成之后,执行到setTimeout方法,这是js的方法,想了解的同学可以自查。之后调用_callWVJBCallbacks,在_callWVJBCallbacks中操作具体见注释
        setTimeout(_callWVJBCallbacks, 0);
    
        function _callWVJBCallbacks() {
            // 获取WVJBCallbacks属性,此属性在Web端添加的代码中有初始化,并将callback添加到WVJBCallbacks中
            var callbacks = window.WVJBCallbacks;
           // 清空WVJBCallbacks
            delete window.WVJBCallbacks;
           // 执行每一个callback,上面代码中的callback是一个function(bridge),调用此function需要传入参数bridge,所以下面代码中将WebViewJavascriptBridge传入
            for (var i=0; i<callbacks.length; i++) {
                callbacks[i](WebViewJavascriptBridge);
            }
        }
    /*
    //执行calback
     function(bridge) {
               // registerHandler(handleName , handle ) 在初始化WebViewJavascriptBridge时就声明,并在WebViewJavascriptBridge_JS.m文件中实现。
               bridge.registerHandler(
                               // handleName  
                               'JS Echo', 
                               // handle
                               function(data, responseCallback) {
                                    console.log("JS Echo called with:", data)
                                    responseCallback(data) 
                                 }
                       )
             }
    
    // registerHandler方法就是将handle保存起来
    function registerHandler(handlerName, handler) {
            messageHandlers[handlerName] = handler; 
        }
    
    */
    
    • 执行完成以上以上以上···所有的代码后Web端的bridge已经创建完了,可以用了。程序接着往下运行,又回到了Web中添加的那个setupWebViewJavascriptBridge(callback) 方法。
    
    function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
    
        /*没错,执行完成以上以上以上···所有的代码后,该执行下面这句代码了,一切源于WVJBIframe
         发起了一个https://__bridge_loaded__请求,那么一切也以它结束,移除WVJBIframe。到此
         刻bridge已经创建,并且有可能将Web端需要注册和OC端注册的事件保存完成,等待调用*/
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
    
    

    4.Web 调用 OC 实现过程

    此时此刻,双方的bridge通讯兵已经创建完成。那么现在有一个任务出现了,Web端想让OC端买个苹果。当然这个任务不是随机想到的,其实事先预定好的,Web和OC约定好一个任务代号:buy_apple,任务实质是买苹果。
    ok,首先OC端先给自己的bridge报备一下,有一个代号为buy_apple,内容买苹果的任务,即:注册任务。

    // 注册任务
    [_bridge registerHandler:@"buy_apple" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"buy_apple called: %@", data);
            ······
            responseCallback(@"买完了,你吃吗?");
        }];
    
    // registerHandler方法将handler保存在数组messageHandlers中
    - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
        _base.messageHandlers[handlerName] = [handler copy];
    }
    

    现在OC端已经准备好了,随时等待Web端来调用。好的,时机已到!Web端把bridge喊道跟前,让他告诉OC端给老子买个苹果,快!于是乎。。。

    // Web端bridge调用callHandler方法,handlerName是约定好的buy_apple,同时传入data(去哪里买),以及OC买完之后的回调。
    bridge.callHandler('buy_apple', {'address': '北京新发地'}, function(response) {
                    log('JS got response', response)
                })
    
    
    以下方法均在WebViewJavascriptBridge_JS.m文件中JS代码中。
    
    // bridge调用后传进来 handlerName, data, responseCallback(handler调用成功后的回调)
    function callHandler(handlerName, data, responseCallback) {
            //因为js是弱语言,参数没有规定都必须传,所以在此判断参数是否对应。
            if (arguments.length == 2 && typeof data == 'function') {
                responseCallback = data;
                data = null;
            }
            // 将handlerName和data封装在一起变为一个message参数,再带上responseCallback参数,调用_soSend 方法
            _doSend({ handlerName:handlerName, data:data }, responseCallback);
        }
    
    //  _doSend函数
    function _doSend(message, responseCallback) {
            // 判断responseCallback是否存在,如果存在则生成一个callbackId,以callbackId为key值,以responseCallback为value存到Web的全局变量responseCallbacks中。
            if (responseCallback) {
                var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
                responseCallbacks[callbackId] = responseCallback;
                 // 同时将callbackId添加给message中做个元素
                message['callbackId'] = callbackId;
            }
            // 将message保存在全局变量sendMessageQueue中。
            sendMessageQueue.push(message);
           // 利用messagingIframe.src,发起一个请求(url:https://__wvjb_queue_message__)
            messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
        }        
    

    ok ,执行至此,Web端的bridge捞起电话☎️“摩西摩西。。我是Web!”,OC端的bridge接到电话。

    // 拦截Web端的request,分析url
    - (BOOL)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]) {//判断url后运行至此,接通了☎️
                /*OC端bridge问了一句话,什么话呢?
                就是:[_base webViewJavascriptFetchQueyCommand];
                跳到该方法发现是:@"WebViewJavascriptBridge._fetchQueue();"
                也是让Web端的bridge执行_fetchQueue();
                _fetchQueue()执行了什么见下面JS代码,Web端bridge回了话并值赋值给了messageQueueString
                */
                NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
                // OC端bridge拿到了话,去调用flushMessageQueue方法做分析
                [_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;
        }
    }
    
    *******JS代码********
    function _fetchQueue() {
            // 将sendMessageQueue存放的messgae转成一个json字符串告诉OC端的bridge
            var messageQueueString = JSON.stringify(sendMessageQueue);
            sendMessageQueue = [];
            return messageQueueString;
        }
    

    Web端的bridge打电话告诉OC端的bridge一段信息,但是这段信息需要去分析一下,才能明白要做什么,他是怎么分析的呢?

    // 分析返回的json字符串
    - (void)flushMessageQueue:(NSString *)messageQueueString{
        // 判断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;
        }
         
        // 反序列化 message 得一个数组
        id messages = [self _deserializeMessageJSON:messageQueueString];
        
        // 循环这个数组
        for (WVJBMessage* message in messages) {
    
            // 判断成员是否为字典
            if (![message isKindOfClass:[WVJBMessage class]]) {
                NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
                continue;
            }
    
            /*responseId到底是干什么的?
             上文提到,Web端调用_doSend方法时,将responseCallback以callbackId
             存入responseCallbacks,那么买苹果这事儿执行完,要执行这个回调事件responseCallback
             告诉Web执行完了,给你个苹果。那这个回调事件呢,需要从responseCallbacks中根据
             callbackId找出来,然后执行。但是responseCallback,应该对应responseId才算工整是吧?
             同时也是区分对面的通讯兵给的是callback的message还是response的message。
             所以在解析callback的message时,会创建一个responseId,并将callbackId的值赋于它。*/ 
         
            // 获取responseId
            NSString* responseId = message[@"responseId"];
           // 根据responseId是否存在判断,对面的通讯兵给的是callback的message还是response的message。
            if (responseId) {
                // 如果是response的message,则根据responseId找到之前保存的responseCallback
                WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
               //将handler执行成功后返回的数据作为responseCallback回调的参数,执行responseCallback
                responseCallback(message[@"responseData"]);
               // 从self.responseCallbacks中删除已执行responseCallback
                [self.responseCallbacks removeObjectForKey:responseId];
            } else {
    // 如果不存在responseId必定是callback的message
    // 准备一个responseCallback,创造一个responseId用于在callback执行完成之后,告诉Web的bridge,并将他需要的东西给他。
                WVJBResponseCallback responseCallback = NULL;
    // 取出callbackId,如果callbackId存在则赋值给responseId
                NSString* callbackId = message[@"callbackId"];
                if (callbackId) {
    // 初始化responseCallback
                    responseCallback = ^(id responseData) {
                        if (responseData == nil) {
                            responseData = [NSNull null];
                        }
                        
                        WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                        [self _queueMessage:msg];
                    };
                } else {
                    responseCallback = ^(id ignoreResponseData) {
                        // Do nothing
                    };
                }
                
                //取出handlerName,然后根据handlerName从全局变量self.messageHandlers找到相对应的handler,然后执行。
                WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
                // 判断是否存在handler
                if (!handler) {
                    NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                    continue;
                }
                // handler有固定的格式,即参数1:data ,参数2:回调block
                handler(message[@"data"], responseCallback);
    /*
       回忆注册任务时的handler,现在执行handler,message[@"data"] = {'address': '北京新发地'},
       所以NSLog打印出来是:buy_apple called: {'address': '北京新发地'},然后调用了回调responseCallback,
       并传入参数@“买完了,你吃吗?”
    
    [_bridge registerHandler:@"buy_apple" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"buy_apple called: %@", data);
            ······
            responseCallback(@"买完了,你吃吗?");
        }];
    */
            }
        }
    }
    
    

    代码运行至此,OC端已经根据Web端的bridge说的信息,找到了handler后并执行了,执行之后OC端有执行了自己创造的responseCallback,那么我们具体看一下这个responseCallback

     responseCallback = ^(id responseData) {
                        if (responseData == nil) {
                            responseData = [NSNull null];
                        }
                        // 将回调的 信息同样做成一个messge。
                        WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                        // 将拼好的message处理成转义后的json字符串
                        [self _queueMessage:msg];
                    };
                } else {
                    responseCallback = ^(id ignoreResponseData) {
                        // Do nothing
                    };
        }
    
    // 所有OC端bridge给Web的bridge发送的信息都要经过此方法,转成一个转义后的json字符串,就像Web的bridge发给OC的bridge也是一段json字符串。
    - (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"];
        // OC的bridge将下面这个javascriptCommand交给了Web的bridge,Web的bridge拿到javascriptCommand后也会进行分析一下,获取到OC端传来的数据后,再执行操作。
        NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
        if ([[NSThread currentThread] isMainThread]) {
            [self _evaluateJavascript:javascriptCommand];
    
        } else {
            dispatch_sync(dispatch_get_main_queue(), ^{
                [self _evaluateJavascript:javascriptCommand];
            });
        }
    }
    

    Web的bridge执行了function _dispatchMessageFromObjC {····},但是最终运行了下面方法,查看下面方法后,其实与OC端的flushMessageQueue类似,这也印证了,OC和Web的bridge使用了相同的分析方法,分析对方传回来的message。如下代码会从message中取出responseId,根据responseId取出对应的responseCallback,然后将message中的data作为responseCallback的参数,运行responseCallback。最终执行log('JS got response', response),打印出信息。

    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 (!handler) {
                        console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
                    } else {
                        handler(message.data, responseCallback);
                    }
                }
            }
        }
    
    

    5. OC 调用 Web 实现过程

    通过上面详细讲述的Web调用 OC 实现过程可知,OC 调用 Web 实现过程也是一样的,只不过是互转了一下角色而已。
    1、 预先定好一个 handlerName
    2、 Web端以handlerName注册一个任务,以备OC调用
    3、OC端根据handlerName调用Web的bridge
    4、剩下就是两个bridge之间的交流了

    相关文章

      网友评论

          本文标题:WebViewJavascriptBridge建立bridge、

          本文链接:https://www.haomeiwen.com/subject/khasnctx.html