示例
-
js侧发起获取当前网络状态的请求
-
Native侧负责取值并返回结果
Javascript
invoke callHandler method
$(".btn_network_info").click(function() {
console.info("btn_network_info clicked");
window.WebViewJavascriptBridge.callHandler(
"JsCallNativeMethod",
{
action: "getNetworkInfo",
needResult: true
},
function(result) {
alert(JSON.stringify(result));
}
);
});
The callHandler definiton
function callHandler(handlerName, data, responseCallback) {
console.info("callHandler handleName=" + handlerName + ";data=" + JSON.stringify(data));
_doSend({
handlerName: handlerName,
data: data
}, responseCallback);
}
- handlerName和data组成message,然后调用
_doSend
the _doSend
definition
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message.callbackId = callbackId;
}
console.info("_doSend message=" + JSON.stringify(message) + ";callbackId=" + message.callbackId);
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
console.info("_doSend set messagingUrl=" + (messagingIframe.src))
}
-
如何responseCallback存在,生成callbackId,全局保存到
responseCallbacks
中,并为message设置callbackId -
将上述生成的message全局保存到
sendMessageQueue
-
设置messagingIframe的src为
CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
,固定值
Native
- native端利用webview的
shouldOverrideUrlLoading
监听iframe的src值的改变
private fun innerShouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
debugMessage("innerShouldOverrideUrlLoading", "url", url,
"webview.info", view?.toSimpleString())
return if (url != null) {
when {
url.startsWith(JS_BRIDGE_RETURN_DATA_PREFIX) -> {
bridgeWebView.handlerReturnData(url.decodeUTF8())
true
}
url.startsWith(JS_BRIDGE_OVERRIDE_URL_PREFIX) -> {
bridgeWebView.flushMessageQueue()
true
}
else -> false
}
} else {
false
}
}
-
当检测到时
yy://__QUEUE_MESSAGE__/
,接下来则从Java侧调用js代码,拉取数据 -
当检测到是
yy://return/_fetchQueue/****
,则表示从js侧拉取了数据,负责解析处理
调用js方法获取数据
/**
* 刷新消息队列
*/
public void flushMessageQueue() {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
@Override
public void onCallBack(String data) {
LogUtil.debugMessage("", "flushMessageQueue",
}
});
}
}
执行js方法,保存responseCallbacks(后期解析数据时使用)
public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
this.loadUrl(jsUrl);
// 添加至 Map<String, CallBackFunction>
String functionName = BridgeUtil.parseFunctionName(jsUrl);
LogUtil.debugMessage("", "BridgeWebView",
"jsUrl", jsUrl, "functionName", functionName);
responseCallbacks.put(functionName, returnCallback);
}
Javascript 准备并返回请求数据
function _fetchQueue() {
console.log("_fetchQueue start")
if(sendMessageQueue.length == 0) return;
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
console.log("_fetchQueue messageQueued=" + messageQueueString);
//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);
console.log("_fetchQueue set bizMessagingIframe")
}
-
如果没有消息,则不继续执行
-
读取当前的
sendMessageQueue
,并置空 -
把所有的请求数据处理,设置到
bizMessagingIframe
的src
上
Native监听请求数据
- native接收数据,解析数据
/**
* 获取到CallBackFunction data执行调用并且从数据集移除
* @param url
*/
public void handlerReturnData(String url) {
//fetchQueue will be the function name
String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
CallBackFunction f = responseCallbacks.get(functionName);
String data = BridgeUtil.getDataFromReturnUrl(url);
LogUtil.debugMessage("", "handlerReturnData", "functionName",
functionName, "callbackFunction", f,
"data", data);
if (f != null) {
f.onCallBack(data);
responseCallbacks.remove(functionName);
return;
}
}
然后进行业务分发处理
Native返回数据
- 调用callbackFunction的方法
responseFunction = new CallBackFunction() {
@Override
public void onCallBack(String data) {
LogUtil.debugMessage("","onCallback","not empty callbackId", callbackId);
Message responseMsg = new Message();
responseMsg.setResponseId(callbackId);
responseMsg.setResponseData(data);
queueMessage(responseMsg);
}
}
/**
* list<message> != null 添加到消息集合否则分发消息
* @param m Message
*/
private void queueMessage(Message m) {
if (startupMessage != null) {
startupMessage.add(m);
} else {
dispatchMessage(m);
}
}
/**
* 分发message 必须在主线程才分发成功
* @param m Message
*/
public void dispatchMessage(Message m) {
String messageJson = m.toJson();
//escape special characters for json string 为json字符串转义特殊字符
messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");
messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");
messageJson = messageJson.replaceAll("(?<=[^\\\\])(\')", "\\\\\'");
String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
// 必须要找主线程才会将数据传递出去 --- 划重点
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
WebViewUtil.evaluateJs(this, javascriptCommand);
}
}
Javascript 响应结果返回
//提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
function _handleMessageFromNative(messageJSON) {
console.log('_handleMessageFromNative:' + JSON.stringify(messageJSON));
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON);
console.log('_handleMessageFromNative pushed into receiveMessageQueue');
}
_dispatchMessageFromNative(messageJSON);
}
//提供给native使用,
function _dispatchMessageFromNative(messageJSON) {
console.log("_dispatchMessageFromNative json=" + JSON.stringify(messageJSON))
setTimeout(function() {
var message = JSON.parse(messageJSON);
var responseCallback;
//java call finished, now need to call js callback function
if (message.responseId) {
console.info("_dispatchMessageFromNative the java side has finished the work");
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
var responseData = message.responseData;
if(typeof responseData == 'string') {
responseData = JSON.parse(responseData);
}
responseCallback(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);
}
}
}
});
}