乐趣区

WebViewJavascriptBridge源码探秘下

上一篇写了 WebViewJavascriptBridge 的 OC 调用 JS 的部分,这篇主要看看 JS 是怎么调用 OC 方法的。

一、OC 注册方法等待 JS 调用

// 注册 handler 等待 js 调用
[_bridge registerHandler:@"OC-fun1" handler:^(id data, WVJBResponseCallback responseCallback) {//NSLog(@"testObjcCallback called: %@", data);
    // 不调用这个方法,js 中调用 OC 方法传递的函数不会被调用,“JS 调用 OC 的返回值”不会被打印。responseCallback(@"OC 发给 JS 的返回值");
}];
//WKWebViewJavascriptBridge.m
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {_base.messageHandlers[handlerName] = [handler copy];
}
只是把 handler 放到 WebViewJavascriptBridgeBase 对象的字典 messageHandlers 里。

二、JS 中是如何调用 OC 方法的

1. 先看页面中按钮的点击处理方法

document.getElementById('buttons').onclick = function(e) {e.preventDefault()
    var params =  {'JS 调用 OC 参数': '参数值'};
    log('JS 马上调用 OC 方法',params)
    // 因其在 function callback(bridge) 定义,所以 bridge 是存在的。第三个参数是个回调函数。bridge.callHandler('OC-fun1',params, function(response) {log('JS 调用 OC 的返回值', response)
    })
}
//WebViewJavascriptBridge_JS.m
function callHandler(handlerName, data, responseCallback) {if (arguments.length == 2 && typeof data == 'function') {
        responseCallback = data;
        data = null;
    }
    _doSend({handlerName:handlerName, data:data}, responseCallback);
}
// 这个函数和上一篇中调用回调的方法是一样的。JS 调用 OC 都是通过修改 iframe 的 url 实现。不同的是这里传递了 responseCallback 参数,这个其实就是要调用 JS 的回调函数 (类似 OC 的 block)。function _doSend(message, responseCallback) {if (responseCallback) {
        // 生成方法对应的 id 值,存到对象里 key 是 callbackId,值是 responseCallback 回调函数。// 再把这个方法的 id 值,放到 message 里,供 OC 通过_fetchQueue 这个 js 方法获取 (通过 evaluateJavaScript)。var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
        responseCallbacks[callbackId] = responseCallback;
        message['callbackId'] = callbackId;
    }
    sendMessageQueue.push(message);
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
// 这里又到了 WKWebViewJavascriptBridge 里面 webView:decidePolicyForNavigationAction: decisionHandler 方法。在里面会调用 [self WKFlushMessageQueue],我们继续看代码
//WKWebViewJavascriptBridge.m
- (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];
    }];
}
//WebViewJavascriptBridgeBase.m
 (void)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;
    }
    //JSON 字符串反序列化为数组,数组里的元素是字典类型。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 会传递 responseId,JS 调用 OC 传递的是 callbackId。NSString* responseId = message[@"responseId"];
        if (responseId) {WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            // 这里调用了 OC 调用 JS 方法时传入的 block。responseCallback(message[@"responseData"]);
             // 调用结束及时移除这个 block
            [self.responseCallbacks removeObjectForKey:responseId];
        } else {
            WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            if (callbackId) {
                //responseCallback 这个 block,是用来调用 JS 调用 OC 时传入的函数的。responseCallback = ^(id responseData) {if (responseData == nil) {responseData = [NSNull null];
                    }
                    
                    WVJBMessage* msg = @{@"responseId":callbackId, @"responseData":responseData};
                    [self _queueMessage:msg];
                };
            } else {responseCallback = ^(id ignoreResponseData) {// Do nothing};
            }
            
            // 通过 message[@"handlerName"] 这里面存到是 OC 的函数名,作为 key 在字典 messageHandlers 中,找到对应的 block。WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
            
            if (!handler) {NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                continue;
            }
            
            // 调用了 OC 注册的方法。responseCallback 在上面已经定义了,用来回调 JS 方法里的函数的。handler(message[@"data"], responseCallback);
        }
    }
}

给 JS 传值,也就是调用 JS 方法里的那个函数。这通过 responseCallback 完成,这和上篇的代码很想。

- (void)_queueMessage:(WVJBMessage*)message {if (self.startupMessageQueue) {[self.startupMessageQueue addObject:message];
    } else {[self _dispatchMessage:message];
    }
}
// 把消息发送给 WEB 环境  yuxg
- (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];
        });
    }
}
//WebViewJavascriptBridge_JS.m
    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;

            //alert("no:"+message.responseId);
            
            
            if (message.responseId) {
                //JS 调用原生的方法走这个分支。responseCallback = responseCallbacks[message.responseId];
                if (!responseCallback) {return;}
                // 这里调用了 JS 方法调用原生的方法是传入的函数。得到 OC 传给 JS 的返回值整个过程结束。responseCallback(message.responseData);
                // 执行过后把对应的内容删除
                delete responseCallbacks[message.responseId];
            } else {//alert(message.callbackId);        //objc_cb_1 objc_cb_2 objc_cb_3....
                
                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);
                }
            }
        }
    }
退出移动版