详细精确阐述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执行流程的文章》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

An Introduction to Probability Theory and Its Applications

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》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具