詳細精確闡述jsBridge執行流程的文章

詳細精確闡述jsBridge執行流程的文章

jsBridge是在webclient的shouldOverrideUrlLoading方法攔截url,經過解析url內的僞協議來實現Native與JS之間的數據傳輸。javascript

經過更改Html 的iFrame標籤的src觸發一個url請求。java

Native端被直接觸發的方法

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);
}
複製代碼

JS端被直接觸發的方法JS端被直接觸發的方法

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); } } 複製代碼

Native訪問JS

一、Native-send

如有回調則生成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);
        }
    }
複製代碼
二、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;
    }
複製代碼
三、Native-flushMessageQueue

經過LoadUrl ("_fetchQueue")觸發JS的_fetchQueue方法。ide

四、JS-_fetchQueue

把已經保存的handler執行結果拼接到Url上並觸發一個網絡請求,url格式以"return/_fetchQueue/"開頭。函數

五、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

一、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;
    }
複製代碼
二、Native-flushMessageQueue

先註冊一個名字爲_fetchQueue的回調。去獲取保存到JS端數組的數據。(代碼請看上面)

三、JS-_fetchQueue

把保存到本地數組的數據拼接到以"return/_fetchQueue/"開頭的Url上並觸發網絡請求。(代碼請看上面)

四、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);
                    }
                }
複製代碼
五、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];
} 
複製代碼

總結:

一、handlerReturnData和_fetchQueue配合使用,處理從JS返回數據到Native。 二、經過判斷responseId是否爲空來斷定當前是回調註冊流程仍是回調執行流程。 三、Native給JS的數據直接拼到Url就能夠傳過去,JS給Native的數據須要保存到JS本地,而後通知Native來取。

最後,但願有講清楚對你們有幫助。有疑問的地方必定要幫我指出來。

相關文章
相關標籤/搜索