Android Developer

JsBridge简单流程

示例

  • js侧发起获取当前网络状态的请求

  • Native侧负责取值并返回结果

Javascript

invoke callHandler method

$(".btn_network_info").click(function() {
        console.info("btn_network_info clicked");
        window.WebViewJavascriptBridge.callHandler(
          "JsCallNativeMethod",
          {
            action: "getNetworkInfo",
            needResult: true
          },
          function(result) {
            alert(JSON.stringify(result));
          }
        );
      });

The callHandler definiton

    function callHandler(handlerName, data, responseCallback) {
        console.info("callHandler handleName=" + handlerName + ";data=" + JSON.stringify(data));
        _doSend({
            handlerName: handlerName,
            data: data
        }, responseCallback);
    }
  • handlerName和data组成message,然后调用_doSend

the _doSend definition

    function _doSend(message, responseCallback) {
        if (responseCallback) {
            var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            responseCallbacks[callbackId] = responseCallback;
            message.callbackId = callbackId;
        }
	console.info("_doSend message=" + JSON.stringify(message) + ";callbackId=" + message.callbackId);
        sendMessageQueue.push(message);
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
	console.info("_doSend set messagingUrl=" + (messagingIframe.src))    
    }
  • 如何responseCallback存在,生成callbackId,全局保存到responseCallbacks中,并为message设置callbackId

  • 将上述生成的message全局保存到sendMessageQueue

  • 设置messagingIframe的src为CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE,固定值

Native

  • native端利用webview的shouldOverrideUrlLoading监听iframe的src值的改变
private fun innerShouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        debugMessage("innerShouldOverrideUrlLoading", "url", url,
            "webview.info", view?.toSimpleString())
        return if (url != null) {
            when {
                url.startsWith(JS_BRIDGE_RETURN_DATA_PREFIX) -> {
                    bridgeWebView.handlerReturnData(url.decodeUTF8())
                    true
                }
                url.startsWith(JS_BRIDGE_OVERRIDE_URL_PREFIX) -> {
                    bridgeWebView.flushMessageQueue()
                    true
                }
                else -> false
            }
        } else {
            false
        }
    }
  • 当检测到时yy://__QUEUE_MESSAGE__/,接下来则从Java侧调用js代码,拉取数据

  • 当检测到是yy://return/_fetchQueue/****,则表示从js侧拉取了数据,负责解析处理

调用js方法获取数据

/**
     * 刷新消息队列
     */
    public void flushMessageQueue() {
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {

                @Override
                public void onCallBack(String data) {
                    LogUtil.debugMessage("", "flushMessageQueue",
             
                }
            });
        }
    }

执行js方法,保存responseCallbacks(后期解析数据时使用)

public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
        this.loadUrl(jsUrl);
        // 添加至 Map<String, CallBackFunction>
        String functionName = BridgeUtil.parseFunctionName(jsUrl);
        LogUtil.debugMessage("", "BridgeWebView",
                "jsUrl", jsUrl, "functionName", functionName);
        responseCallbacks.put(functionName, returnCallback);
    }

Javascript 准备并返回请求数据

function _fetchQueue() {
	console.log("_fetchQueue start")    
        if(sendMessageQueue.length == 0) return;
        var messageQueueString = JSON.stringify(sendMessageQueue);
        sendMessageQueue = [];
	console.log("_fetchQueue messageQueued=" + messageQueueString);    
        //android can't read directly the return data, so we can reload iframe src to communicate with java
        bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
    	console.log("_fetchQueue set bizMessagingIframe")
}
  • 如果没有消息,则不继续执行

  • 读取当前的sendMessageQueue,并置空

  • 把所有的请求数据处理,设置到bizMessagingIframesrc

Native监听请求数据

  • native接收数据,解析数据
/**
     * 获取到CallBackFunction data执行调用并且从数据集移除
     * @param url
     */
    public void handlerReturnData(String url) {
        //fetchQueue will be the function name
        String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
        CallBackFunction f = responseCallbacks.get(functionName);
        String data = BridgeUtil.getDataFromReturnUrl(url);
        LogUtil.debugMessage("", "handlerReturnData", "functionName",
                functionName, "callbackFunction", f,
                "data", data);
        if (f != null) {
            f.onCallBack(data);
            responseCallbacks.remove(functionName);
            return;
        }
    }

然后进行业务分发处理

Native返回数据

  • 调用callbackFunction的方法
responseFunction = new CallBackFunction() {
    @Override
    public void onCallBack(String data) {
        LogUtil.debugMessage("","onCallback","not empty callbackId", callbackId);
        Message responseMsg = new Message();
        responseMsg.setResponseId(callbackId);
        responseMsg.setResponseData(data);
        queueMessage(responseMsg);
    }
}
/**
     * list<message> != null 添加到消息集合否则分发消息
     * @param m Message
     */
    private void queueMessage(Message m) {
        if (startupMessage != null) {
            startupMessage.add(m);
        } else {
            dispatchMessage(m);
        }
    }

    /**
     * 分发message 必须在主线程才分发成功
     * @param m Message
     */
    public 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("(?<=[^\\\\])(\')", "\\\\\'");
        String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
        // 必须要找主线程才会将数据传递出去 --- 划重点
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            WebViewUtil.evaluateJs(this, javascriptCommand);
        }
    }

Javascript 响应结果返回

 //提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
    function _handleMessageFromNative(messageJSON) {
        console.log('_handleMessageFromNative:' + JSON.stringify(messageJSON));
        if (receiveMessageQueue) {
            receiveMessageQueue.push(messageJSON);
      console.log('_handleMessageFromNative pushed into receiveMessageQueue');
        }
        _dispatchMessageFromNative(messageJSON);
    }
//提供给native使用,
    function _dispatchMessageFromNative(messageJSON) {
  console.log("_dispatchMessageFromNative json=" + JSON.stringify(messageJSON))
        setTimeout(function() {
            var message = JSON.parse(messageJSON);
            var responseCallback;
            //java call finished, now need to call js callback function
            if (message.responseId) {
    console.info("_dispatchMessageFromNative the java side has finished the work");
                responseCallback = responseCallbacks[message.responseId];
                if (!responseCallback) {
                    return;
                }
                var responseData = message.responseData;
                if(typeof responseData == 'string') {
                    responseData = JSON.parse(responseData);
                }
                responseCallback(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);
                    }
                }
            }
        });
    }