WebViewJavascriptBridge 原理解析

栏目: JavaScript · 发布时间: 5年前

内容简介:WebViewJavascriptBridge是项目中常用的OC与js交互的第三方框架,它没有通过苹果的JavascriptCore框架来实现,而是实现了自己的逻辑。下面看一下Message的数据结构,当然在传送的过程中是一JSON的格式进行传送的,以OC为例:

WebViewJavascriptBridge是项目中常用的OC与js交互的第三方框架,它没有通过苹果的JavascriptCore框架来实现,而是实现了自己的逻辑。

WebViewJavascriptBridgeBase 是OC模块的功能实现类,对应的js功能类为 WebViewJavascriptBridge_JS ,此处巧妙地使用字符串来解决了之前版本集成打包需要bundle加载js模块的问题。

先说明几个数据结构:

  • @property (strong, nonatomic) NSMutableDictionary messageHandlers; ,以字典的形式保存注册的Hander,js相似数据结构为: var messageHandlers = {};
  • NSMutableDictionary* responseCallbacks; ,以字典的形式保存调用Handler后回调给js消息的Block,js相似数据结构为: var responseCallbacks = {};

下面看一下Message的数据结构,当然在传送的过程中是一JSON的格式进行传送的,以OC为例:

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;
    }
复制代码

data :要传递的数据(如参数)。

callbackId : 唯一标示作为responseCallBack的Key

handerName :Handler唯一标示(Key)

对应js代码:

_doSend({ handlerName:handlerName, data:data }, responseCallback);

function _doSend(message, responseCallback) {
	if (responseCallback) {
		var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
		responseCallbacks[callbackId] = responseCallback;
		message['callbackId'] = callbackId;
	}
复制代码

发送消息,OC为调用WebView调用 evaluateJavascript 方法来调用js,通过WebViewJavascriptBridge来调用js的 _handleMessageFromObjC方法 主要代码:

- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    [self _log:@"SEND" json:messageJSON];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
    <-------------省略-------------->
    
    NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
    if ([[NSThread currentThread] isMainThread]) {
        [self _evaluateJavascript:javascriptCommand];

    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self _evaluateJavascript:javascriptCommand];
        });
    }
}
复制代码

而js调用OC则是通过WebView加载Request来实现:

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;
	}
复制代码
  1. 添加Message到待处理消息集合MessageQueue,然后调用 messagingIframe.src 来加载Request,格式为: https://__wvjb_queue_message__
- (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]) {
            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;
    }
}
复制代码

2.获取要处理的js消息,进行处理,先说一下代码里的判断函数

isWebViewJavascriptBridgeURL :判断是否是Bridge处理的URL格式,为了兼容旧版本,判断了2个Scheme,然后继续下面2个函数的判断。

isBridgeLoadedURL :判断是否是Bridge初始化加载URL格式,对应格式为: https://__bridge_loaded__ ,如果是,则调用injectJavascriptFile函数,加载初始化的js文件(WebViewJavascriptBridge_JS)

isQueueMessageURL :判断是否是Bridge发送消息的URL格式,对应格式为: https://__wvjb_queue_message__

当url是 https://__wvjb_queue_message__ ,则表示js有消息要处理,WebView加载Request,调用OC [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]] 这个方法来调用js文件中的

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

方法,来获取要处理的消息,然后调用OC的 flushMessageQueue: 方法来处理消息

处理消息,OC处理js方法为 - (void)flushMessageQueue:(NSString *)messageQueueString{xxx}

先看这部分代码:

WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            if (callbackId) {
                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 (!handler) {
                NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                continue;
            }
            
            handler(message[@"data"], responseCallback);
复制代码

如果存在callbackId,则代表js存在responseCallBack,需要OC回调,所以需要发送回调消息即`WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; [self _queueMessage:msg];只是callbackId的KEY设置成responseId来区分是回调。 再看代码:

NSString* responseId = message[@"responseId"];
        if (responseId) {
            WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            responseCallback(message[@"responseData"]);
            [self.responseCallbacks removeObjectForKey:responseId];
        }
复制代码

首先判断是否有responseId(即OC的ResponseCallBack保存时候的CallbackId),如果有,那么代表这个消息是js的回调消息(OC调用js注册的Handler),则直接处理。

如果我的文章对你有所帮助,请留言告诉我,Thanks!


以上所述就是小编给大家介绍的《WebViewJavascriptBridge 原理解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Defensive Design for the Web

Defensive Design for the Web

37signals、Matthew Linderman、Jason Fried / New Riders / 2004-3-2 / GBP 18.99

Let's admit it: Things will go wrong online. No matter how carefully you design a site, no matter how much testing you do, customers still encounter problems. So how do you handle these inevitable bre......一起来看看 《Defensive Design for the Web》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换