jsBridge是在webclient的shouldOverrideUrlLoading方法攔截url,經過解析url內的僞協議來實現Native與JS之間的數據傳輸。javascript
經過更改Html 的iFrame標籤的src觸發一個url請求。java
a、handlerReturnData(url):處理JS返回處理結果到Native的方法,此方法會解析url內的handlerName和Message。android
/**
* 獲取到CallBackFunction data執行調用而且從數據集移除
* @param url
*/
void handlerReturnData(String url) {
String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
CallBackFunction f = responseCallbacks.get(functionName);
String data = BridgeUtil.getDataFromReturnUrl(url);
if (f != null) {
f.onCallBack(data);
responseCallbacks.remove(functionName);
return;
}
}
複製代碼
b、flushMessageQueue:處理JS主動發起對Native方法的調用的方法,此方法是觸發一個在主線程執行的LoadUrl從JS端獲取web
/**
* 刷新消息隊列
*/
void flushMessageQueue() {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
@Override
public void onCallBack(String data) {
// deserializeMessage 反序列化消息
List<Message> list = null;
try {
list = Message.toArrayList(data);
} catch (Exception e) {
e.printStackTrace();
return;
}
if (list == null || list.size() == 0) {
return;
}
for (int i = 0; i < list.size(); i++) {
Message m = list.get(i);
String responseId = m.getResponseId();
// 是不是response CallBackFunction
if (!TextUtils.isEmpty(responseId)) {
CallBackFunction function = responseCallbacks.get(responseId);
String responseData = m.getResponseData();
function.onCallBack(responseData);
responseCallbacks.remove(responseId);
} else {
CallBackFunction responseFunction = null;
// if had callbackId 若是有回調Id
final String callbackId = m.getCallbackId();
if (!TextUtils.isEmpty(callbackId)) {
responseFunction = new CallBackFunction() {
@Override
public void onCallBack(String data) {
Message responseMsg = new Message();
responseMsg.setResponseId(callbackId);
responseMsg.setResponseData(data);
queueMessage(responseMsg);
}
};
} else {
responseFunction = new CallBackFunction() {
@Override
public void onCallBack(String data) {
// do nothing
}
};
}
// BridgeHandler執行
BridgeHandler handler;
if (!TextUtils.isEmpty(m.getHandlerName())) {
handler = messageHandlers.get(m.getHandlerName());
} else {
handler = defaultHandler;
}
if (handler != null){
handler.handler(m.getData(), responseFunction);
}
}
}
}
});
}
}
public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
this.loadUrl(jsUrl);
// 添加至 Map<String, CallBackFunction>
responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
}
複製代碼
a、_handleMessageFromNative(messageJSON):分發Native對JS方法的調用事件。方法內直接調用了_dispatchMessageFromNative。json
//提供給native調用,receiveMessageQueue 在會在頁面加載完後賦值爲null,因此
function _handleMessageFromNative(messageJSON) {
console.log(messageJSON);
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON);
}
_dispatchMessageFromNative(messageJSON);
}
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);
}
}
}
});
}
複製代碼
b、_fetchQueue:把從JS端暫存在sendMessageQueue須要返回的數據傳送給Native。數組
// 提供給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 if (messageQueueString !== '[]') { bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString); } } 複製代碼
如有回調則生成callbackId,把callback保存到Native本地,把callbackId拼接到url上。執行LoadUrl訪問JS的_handleMessageFromNative。bash
/**
* 保存message到消息隊列
* @param handlerName handlerName
* @param data data
* @param responseCallback CallBackFunction
*/
private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
Message m = new Message();
if (!TextUtils.isEmpty(data)) {
m.setData(data);
}
if (responseCallback != null) {
String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
responseCallbacks.put(callbackStr, responseCallback);
m.setCallbackId(callbackStr);
}
if (!TextUtils.isEmpty(handlerName)) {
m.setHandlerName(handlerName);
}
queueMessage(m);
}
private void queueMessage(Message m) {
if (startupMessage != null) {
startupMessage.add(m);
} else {
dispatchMessage(m);
}
}
/**
* 分發message 必須在主線程才分發成功
* @param m Message
*/
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("(?<=[^\\\\])(\')", "\\\\\'");
messageJson = messageJson.replaceAll("%7B", URLEncoder.encode("%7B"));
messageJson = messageJson.replaceAll("%7D", URLEncoder.encode("%7D"));
messageJson = messageJson.replaceAll("%22", URLEncoder.encode("%22"));
String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
// 必需要找主線程纔會將數據傳遞出去 --- 劃重點
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
this.loadUrl(javascriptCommand);
}
}
複製代碼
依次按以下順序執行:_handleMessageFromNative->_dispatchMessageFromNative--如有回調則callbackId不爲空-->MessagerHandler.handler--如有回調怎會執行_doSend-->_doSend:responseId不爲空,把handler執行結果保存在JS端的sendMessageQueue。網絡
//直接發送
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);
}
}
複製代碼
//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;
}
複製代碼
經過LoadUrl ("_fetchQueue")觸發JS的_fetchQueue方法。ide
把已經保存的handler執行結果拼接到Url上並觸發一個網絡請求,url格式以"return/_fetchQueue/"開頭。函數
解析得到調用JS代碼的執行結果數據,取出_fetchQueue對應的回調執行,此時responseId不爲空,(注意此時的responseId等於在send裏面生成的callbackId),故此時以responseId爲key取出send時保存在Native本地的回調並執行。
Message m = list.get(i);
String responseId = m.getResponseId();
// 是不是response CallBackFunction
if (!TextUtils.isEmpty(responseId)) {
CallBackFunction function = responseCallbacks.get(responseId);
String responseData = m.getResponseData();
function.onCallBack(responseData);
responseCallbacks.remove(responseId);
}
複製代碼
如有回調則生成callbackId,把回調保存到本地集合,並把要傳輸的數據保存到本地數組,觸發一個網絡請求。
//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;
}
複製代碼
先註冊一個名字爲_fetchQueue的回調。去獲取保存到JS端數組的數據。(代碼請看上面)
把保存到本地數組的數據拼接到以"return/_fetchQueue/"開頭的Url上並觸發網絡請求。(代碼請看上面)
取出_fetchQueue對應的回調執行。--若callback不爲空則生成回調之行代碼-->handler--handler執行完若是有回調-->queueMessage--Message的responseId不爲空-->dispatchMessage--把執行結果數據拼接到script上-->LoadUrl("_handleMessageFromNative");
//直接發送
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);
}
}
複製代碼
此時responseId不爲空,(注意此responseId與_doSend階段生成的callbackId相同),故以responseId爲key取出在_doSend時保存在本地集合的回調並執行。
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];
}
複製代碼
總結:
一、handlerReturnData和_fetchQueue配合使用,處理從JS返回數據到Native。 二、經過判斷responseId是否爲空來斷定當前是回調註冊流程仍是回調執行流程。 三、Native給JS的數據直接拼到Url就能夠傳過去,JS給Native的數據須要保存到JS本地,而後通知Native來取。
最後,但願有講清楚對你們有幫助。有疑問的地方必定要幫我指出來。