共计 5302 个字符,预计需要花费 14 分钟才能阅读完成。
//notation: js file can only use this kind of comments
//since comments will cause error when use in webview.loadurl,
//comments will be remove by java use regexp
(function() {if (window.WebViewJavascriptBridge) {return;}
var messagingIframe;
var bizMessagingIframe;
var sendMessageQueue = [];
var receiveMessageQueue = [];
var messageHandlers = {};
var CUSTOM_PROTOCOL_SCHEME = 'yy';
var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
var responseCallbacks = {};
var uniqueId = 1;
// 创立音讯 index 队列 iframe
function _createQueueReadyIframe(doc) {messagingIframe = doc.createElement('iframe');
messagingIframe.style.display = 'none';
doc.documentElement.appendChild(messagingIframe);
}
// 创立音讯体队列 iframe
function _createQueueReadyIframe4biz(doc) {bizMessagingIframe = doc.createElement('iframe');
bizMessagingIframe.style.display = 'none';
doc.documentElement.appendChild(bizMessagingIframe);
}
//set default messageHandler 初始化默认的音讯线程
function init(messageHandler) {if (WebViewJavascriptBridge._messageHandler) {throw new Error('WebViewJavascriptBridge.init called twice');
}
WebViewJavascriptBridge._messageHandler = messageHandler;
var receivedMessages = receiveMessageQueue;
receiveMessageQueue = null;
for (var i = 0; i < receivedMessages.length; i++) {_dispatchMessageFromNative(receivedMessages[i]);
}
}
// 发送
function send(data, responseCallback) {
_doSend({data: data}, responseCallback);
}
// 注册线程 往数组外面增加值
function registerHandler(handlerName, handler) {messageHandlers[handlerName] = handler;
}
// 调用线程
function callHandler(handlerName, data, responseCallback) {
_doSend({
handlerName: handlerName,
data: data
}, responseCallback);
}
//sendMessage add message, 触发 native 解决 sendMessage
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;
}
// 提供给 native 调用, 该函数作用: 获取 sendMessageQueue 返回给 native, 因为 android 不能间接获取返回的内容, 所以应用 url shouldOverrideUrlLoading 的形式返回内容
function _fetchQueue() {var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
//android can't read directly the return data, so we can reload iframe src to communicate with java
bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
}
// 提供给 native 应用,
function _dispatchMessageFromNative(messageJSON) {setTimeout(function() {var message = JSON.parse(messageJSON);
var responseCallback;
//java call finished, now need to call js callback function
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({
responseId: callbackResponseId,
responseData: responseData
});
};
}
var handler = WebViewJavascriptBridge._messageHandler;
if (message.handlerName) {handler = messageHandlers[message.handlerName];
}
// 查找指定 handler
try {handler(message.data, responseCallback);
} catch (exception) {if (typeof console != 'undefined') {console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
}
}
}
});
}
// 提供给 native 调用,receiveMessageQueue 在会在页面加载完后赋值为 null, 所以
function _handleMessageFromNative(messageJSON) {console.log(messageJSON);
if (receiveMessageQueue) {receiveMessageQueue.push(messageJSON);
}
_dispatchMessageFromNative(messageJSON);
}
var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
init: init,
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
_fetchQueue: _fetchQueue,
_handleMessageFromNative: _handleMessageFromNative
};
var doc = document;
_createQueueReadyIframe(doc);
_createQueueReadyIframe4biz(doc);
var readyEvent = doc.createEvent('Events');
readyEvent.initEvent('WebViewJavascriptBridgeReady');
readyEvent.bridge = WebViewJavascriptBridge;
doc.dispatchEvent(readyEvent);
})();
待过的某家公司中用于和原生交互的计划,当初这个文件也是从网上找的,拿来就用,没怎么细看。
最早咱们的 Hybrid 利用只是 h5 调用原生的一些办法,所以只应用了 native 向页面注入一个 webview 的变量,并在这个变量上挂载一些办法,在安卓上存在一些问题(?安全漏洞)。起初改用 bridge,安全性和兼容性比拟好。bridge 有一个小小的问题就是如果原生端不存在对应名称的办法,h5 无奈晓得,所以 h5 须要做一套版本的判断,再进行办法的调用。
执行的操作
简略剖析一下:这个文件蕴含了一个立刻执行函数,代码加载后,做了几件事件:
- 定义协定 scheme,相似 http,这里定义为 yy
- 创立音讯 index 队列 iframe,这个 iframe 前面能够看到是 h5 端应用的;创立音讯体队列 iframe,这个 iframe 能够看到是给原生端应用的。这两个 iframe 用于消息传递。
- 创立 WebViewJavaScriptBridge 对象并挂载到 window 上
- 创立 Event 对象,初始化事件名为 WebViewJavaScriptBridgeReady 的事件,并触发事件,示意 bridge 对象已筹备好,能够应用了。
bridge 对象
bridge 对象有 6 个办法,别离为 init、send、registerHandler、callHandler、_fetchQueue、_handleMessageFromNative,办法的作用大抵能够见名知意。
- init 办法 ,用于设置默认的音讯处理函数,如果此时 h5 端的音讯接管队列不为空,则调用_dispatchMessageFromNative 来散发解决队列中的音讯
- send 办法 ,用于 h5 端发送音讯,触发 native 去解决,调用_doSend 将音讯塞进 h5 端的音讯发送队列,并将 messagingIframe 的 src 设置为发送的协定,触发 iframe 从新申请加载
- registerHandler 办法 :用于定义音讯名称与音讯回调函数的映射,保留在 messageHandlers 对象上,提供 js 函数给 native 调用,在原生实现操作后,将数据通过回调函数传递给 h5
- callHandler 办法 :用于 h5 调用_doSend 批改 iframe src 的形式将音讯和数据传递原生
- _fetchQueue 办法 :提供给 native 应用的办法,将 bizMessagingIframe 的 src 设置为接管的协定,触发 iframe 从新申请,使原生获取到 h5 发送的音讯
- _handleMessageFromNative 办法 :提供给 native 应用的办法,将音讯塞进 h5 的音讯接管队列,并进行音讯散发
大抵流程
1. H5 调用 Native
2. Native 向 H5 传递
原理
H5 调用 Native 的流程次要利用了 iframe 与原生端进行交互,通过批改 iframe 的 src 来触发原生端调用对应的操作,原生端监听到触发后,通过一系列操作后失去一个后果,再通过调用 h5 的回调函数,使 h5 能够解决原生返回的后果。
Native 向 h5 传递有点相似 h5 的事件监听,h5 注册一个名称用于标记操作,映射一个回调函数,原生端在用户做了某些操作后,获取到 h5 注册的操作对应的回调函数,并进行调用,将操作后果传递给 h5。
参考
https://github.com/lzyzsd/JsB…
luffyjet/WebViewJavaScriptBridge
marcuswestin/WebViewJavascriptBridge
https://blog.csdn.net/weixin_…
https://blog.csdn.net/sinat_3…
Android 与 JS 交互篇 –JSBridge 的应用