前言
WebViewJavascriptBridge(v6.0.3)作为现在混合App使用最为广泛的JSBridge桥接库,这篇文章主要结合源码解析其工作流程。
基本原理
Native 和 JavaScript 的交互一般是通过以下三种方式实现的
1、(UIWebView)Native 通过 -stringByEvaluatingJavaScriptFromString: 执行一段js代码
2、(WKWebView)Native通过 -evaluateJavaScript:方法执行一段js代码
3、JavaScript to Native:在网页中加载一个 Custom URL Scheme 的链接(设置 window.location 或新建一个 iframe 去加载这个 URL),原生在代理方法拦截处理
3、iOS使用JavaScriptCore
WebViewJavascriptBridge 是使用了方式1和2 或者 1和3 来完成的
API
原生
//注册方法,在JS调用原生时调用
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
//调用js注册过的方法,
- - (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
JavaScript
// 注册方法,以方法名为key handler 为值,保存到messageHandlers 中
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
// 调用方法
function callHandler(handlerName, data, responseCallback) {
if (arguments.length == 2 && typeof data == 'function') {
responseCallback = data;
data = null;
}
_doSend({ handlerName:handlerName, data:data }, responseCallback);
}
// 提供给原生调用
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
交互流程
WebViewJavascriptBridge 参与交互的流程包括三个部分:JS端初始化、原生初始化、JS 调用原生、原生调用 JS。
1、JS端初始化:
2、原生初始化(iOS)
3、JS调用原生
1、原生注册
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
注册方法保存到WebViewJavascriptBridgeBase中的messageHandlers字典 方法名为key WVJBHandler block 为值
2、JS端调用全局对象window.webViewJavascriptBridge对象调用初始化注入的方法
function callHandler(handlerName, data, responseCallback)
(注:WebViewJavascriptBridge_JS中的方法,js初始化加载注入)
3、JS把把方法保存到消息数组 sendMessageQueue中
保存格式:
{ "handleName": handleName",
"data": data,
"callbackId": responseCallback
}
4、JS利用 messagingIframe.src = https://__wvjb_queue_message__ 触发iframe 加载发送消息的 URL,原生拦截到 https://__wvjb_queue_message__ 利用 _evaluateJavaScript方法执行JS代码 WebViewJavascriptBridge._fetchQueue();
- (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];
}];
}
5、在JS端全局对象 WebViewJavascriptBridge的_fetchQueue()方法
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
获取到刚才保存到sendMessageQueue消息队列中的参数
6、利用原生注册的方法回调给上层应用
4、原生调用JS
1、JS注册方法
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
保存到messageHandlers字典中
2、原生调用
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
把参数生成message字典
格式:
{ "handlerName": handlerName,
"callbackId": callbackId,
"data":data
}
再调用- (void)_dispatchMessage:(WVJBMessage*)message 方法
把message格式化成json字符串messageJSON 生成js字符串代码
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
利用_webview方法 [_webView evaluateJavaScript:javascriptCommand completionHandler:nil]; 执行JS全局对象中的 _handleMessageFromObjC 方法
3、在js的全局对象中执行 _handleMessageFromObjC 方法,根据原生传入的参数 messageJSON解析出 data, callbackid, handleName
4、根据callbackId 执行回调,根据handleName 在messageHandlers字典中找出js注册的方法,执行js方法,带入data参数
5、js接收到原生的参数,处理相应的逻辑