博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详细精确阐述jsBridge执行流程的文章
阅读量:6494 次
发布时间:2019-06-24

本文共 12002 字,大约阅读时间需要 40 分钟。

详细精确阐述jsBridge执行流程的文章

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
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
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  CallBackFunctionif (!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 functionif (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来取。

最后,希望有讲清楚对大家有帮助。有疑问的地方一定要帮我指出来。

转载于:https://juejin.im/post/5ce26a89e51d4510624f970b

你可能感兴趣的文章
微服务架构springcloud
查看>>
深入剖析Android系统试读样章
查看>>
测试用例出错重跑--flaky插件
查看>>
yaf的安装
查看>>
比较java与C++的不同
查看>>
Twitter Storm入门
查看>>
使用scikit-learn进行文本分类
查看>>
Ansible自动化运维配置与应用(结合实例)
查看>>
下面简要介绍软件工程的七条原理
查看>>
java POI实现excel实现表格导出
查看>>
Lua(三)——语句
查看>>
TensorFlow的基本运算01
查看>>
怎么看电脑有没有安装USB3.0驱动
查看>>
overflow清除浮动的原理
查看>>
Spring Boot 使用parent方式引用时 获取值属性方式默认@
查看>>
Elasticsearch之中文分词器插件es-ik(博主推荐)
查看>>
解决maven下载jar慢的问题(如何更换Maven下载源)
查看>>
linux安装gitLab
查看>>
concurrent包的实现示意图
查看>>
golang os.Args
查看>>