详细精确阐述jsBridge执行流程的文章
栏目: JavaScript · 发布时间: 6年前
内容简介:jsBridge是在webclient的shouldOverrideUrlLoading方法拦截url,通过解析url内的伪协议来实现Native与JS之间的数据传输。通过更改Html 的iFrame标签的src触发一个url请求。a、handlerReturnData(url):处理JS返回处理结果到Native的方法,此方法会解析url内的handlerName和Message。
jsBridge是在webclient的shouldOverrideUrlLoading方法拦截url,通过解析url内的伪协议来实现Native与JS之间的数据传输。
通过更改Html 的iFrame标签的src触发一个url请求。
Native端被直接触发的方法
a、handlerReturnData(url):处理JS返回处理结果到Native的方法,此方法会解析url内的handlerName和Message。
/**
* 获取到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端获取
/**
* 刷新消息队列
*/
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);
}
复制代码
JS端被直接触发的方法JS端被直接触发的方法
a、_handleMessageFromNative(messageJSON):分发Native对JS方法的调用事件。方法内直接调用了_dispatchMessageFromNative。
//提供给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);
}
}
复制代码
Native访问JS
1、Native-send
若有回调则生成callbackId,把callback保存到Native本地,把callbackId拼接到url上。执行LoadUrl访问JS的_handleMessageFromNative。
/**
* 保存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);
}
}
复制代码
2、JS-_handleMessageFromNative
依次按如下顺序执行:_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;
}
复制代码
3、Native-flushMessageQueue
通过LoadUrl ("_fetchQueue")触发JS的_fetchQueue方法。
4、JS-_fetchQueue
把已经保存的handler执行结果拼接到Url上并触发一个网络请求,url格式以"return/_fetchQueue/"开头。
5、Native-handlerReturnData
解析获得调用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);
}
复制代码
JS访问Native
1、JS-_doSend
若有回调则生成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;
}
复制代码
2、Native-flushMessageQueue
先注册一个名字为_fetchQueue的回调。去获取保存到JS端数组的数据。(代码请看上面)
3、JS-_fetchQueue
把保存到本地数组的数据拼接到以"return/_fetchQueue/"开头的Url上并触发网络请求。(代码请看上面)
4、Native-handlerReturnData
取出_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);
}
}
复制代码
5、JS-_handleMessageFromNative
此时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];
}
复制代码
总结:
1、handlerReturnData和_fetchQueue配合使用,处理从JS返回数据到Native。 2、通过判断responseId是否为空来判定当前是回调注册流程还是回调执行流程。 3、Native给JS的数据直接拼到Url就可以传过去,JS给Native的数据需要保存到JS本地,然后通知Native来取。
最后,希望有讲清楚对大家有帮助。有疑问的地方一定要帮我指出来。
以上所述就是小编给大家介绍的《详细精确阐述jsBridge执行流程的文章》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 个人角度阐述 OKR
- 中央企业“三重一大”决策运行系统价值阐述
- 章文嵩阐述滴滴派单复杂度,因言论有误被怼
- 【好文推荐】黑莓OS手册是如何详细阐述底层的进程和线程模型的?
- sqltoy-orm-4.18.21 发版,阐述和分享对 orm 的理解
- 干货!谷歌首席科学家发文阐述“半监督学习革命”,想走出瓶颈先试试这个
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
An Introduction to Probability Theory and Its Applications
William Feller / Wiley / 1991-1-1 / USD 120.00
Major changes in this edition include the substitution of probabilistic arguments for combinatorial artifices, and the addition of new sections on branching processes, Markov chains, and the De Moivre......一起来看看 《An Introduction to Probability Theory and Its Applications》 这本书的介绍吧!