详细精确阐述jsBridge执行流程的文章
栏目: JavaScript · 发布时间: 5年前
内容简介: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 的理解
- 干货!谷歌首席科学家发文阐述“半监督学习革命”,想走出瓶颈先试试这个
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
颠覆式创新:移动互联网时代的生存法则
李善友 / 机械工业出版社 / 2014-12-1 / 69
为什么把每件事情都做对了,仍有可能错失城池?为什么无人可敌的领先企业,却在一夜之间虎落平阳? 短短三年间诺基亚陨落,摩托罗拉区区29亿美元出售给联想,芯片业霸主英特尔在移动芯片领域份额几乎为零,风光无限的巨头转眼成为被颠覆的恐龙,默默无闻的小公司一战成名迅速崛起,令人瞠目结舌的现象几乎都被“颠覆式创新”法则所解释。颠覆式创新教你在新的商业竞争中“换操作系统”而不是“打补丁”,小公司用破坏性思......一起来看看 《颠覆式创新:移动互联网时代的生存法则》 这本书的介绍吧!