WebViewJavascriptBridge 原理

官方demo 交互流程

1.webview 加载 ExampleApp.html

 // ExampleApp.html 中的js代码
function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
       //创建iframe 梦开始的地方
        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)
    }

2.创建 一个 iframe 来触发 原生webView的代理方法,即:

// UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {}

//WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {}
上述两个代理方法调用的核心方法如下:
 //判断是否是自己的声明的scheme
 if ([_base isWebViewJavascriptBridgeURL:url]) {
 	//是否是 'https://__bridge_loaded__' 这个类型的url
    //如果是 就去加载 WebViewJavascriptBridge_JS.m 中的js代码
        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;
   }

到此为止是为了把WebViewJavascriptBridge_JS.m 中的js代码注入Dom中


Native call js

- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
    NSMutableDictionary* message = [NSMutableDictionary dictionary];
    if (data) {
        message[@"data"] = data;
    }
    //如果有回调 生成一个callbackId存到message里,并把回调存到全局变量responseCallbacks
    if (responseCallback) {
        NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
        self.responseCallbacks[callbackId] = [responseCallback copy];
        message[@"callbackId"] = callbackId;
    }
}
// oc调用网页js方法 是通过WebViewJavascriptBridge._handleMessageFromObjC('%@')
// 给js传了一段json 
- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    ...
    NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
    //主线程调用
    [self _evaluateJavascript:javascriptCommand];
}

再看 _handleMessageFromObjC()的实现

//处理来自oc发过来的message(json)
function _doDispatchMessageFromObjC() {
	//解析oc传过来的json
			var message = JSON.parse(messageJSON);
			var messageHandler;
			var responseCallback;
            //js调用oc   oc有回调
			if (message.responseId) {
				responseCallback = responseCallbacks[message.responseId];
				if (!responseCallback) {
					return;
				}
				responseCallback(message.responseData);
				delete responseCallbacks[message.responseId];
			} else {
				//oc调用js  js有回调
				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 {
					//调用已在 messageHandlers中注册的方法, 回调给responseCallback->_doSend
					//_doSend 即js调用oc 
					handler(message.data, responseCallback);
				}
			}
		}

js call oc

调用流程: WebViewJavascriptBridge.callHandler() -> _doSend()

    function callHandler(handlerName, data, responseCallback) {
		if (arguments.length == 2 && typeof data == 'function') {
			responseCallback = data;
			data = null;
		}
		_doSend({ handlerName:handlerName, data:data }, responseCallback);
	}
    // 把js调用oc的参数 封装成message对象 并把回调保存起来
	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;
	}

到这又开始触发到oc webview的代理回调里去了,即这块代码:

if ([_base isWebViewJavascriptBridgeURL:url]) {
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
        	//这次是走的这, 去调用js WebViewJavascriptBridge._fetchQueue();
        	// 取到刚刚push到sendMessageQueue 里的json
            NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
            //处理js传过来的参数 如果有 callbackId 就说明js调oc的时候 oc是有回调的
            [_base flushMessageQueue:messageQueueString];
        } else {
            [_base logUnkownMessage:url];
        }
        return NO;
   }

至此整个调用流程就走通了 O(∩_∩)O哈哈~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值