成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

源碼解讀JS與Native通信原理-WebViewJavascriptBridge

learn_shifeng / 1183人閱讀

摘要:并且最好是在的回調(diào)函數(shù)中調(diào)用,可以保證初始化成功了。當(dāng)我們通知端進行初始化,并且初始化之后,里面會去遍歷中的回調(diào)函數(shù),并將當(dāng)做參數(shù)注入。里面會將里面的回調(diào)函數(shù)保存在全局對象變量中則是自增的。

緣由:網(wǎng)上其實有很多講解WebViewJavascriptBridge原理的文章,但都著重了Native端,今天從一個純前端角度出發(fā),抓住核心脈絡(luò)講解下原理,清晰明了,一文即懂。

通信的基礎(chǔ):

native端到j(luò)s端。native能獲取到window環(huán)境,執(zhí)行JS。

js端到native端。native能截獲H5頁面跳轉(zhuǎn),故而JS端可以通過動態(tài)創(chuàng)建iframe來告訴native,我發(fā)請求了。(其實就是在window下維護了一個messageQueue數(shù)組,然后js創(chuàng)建個iframe,告訴native,我向你發(fā)請求了,但具體是什么請求,url里面是不會體現(xiàn)的,需要native去遍歷messageQueue數(shù)組,畢竟native能取到window環(huán)境 )。

首先初始化JS端環(huán)境。

初始化方法setupWebViewJavascriptBridge,里面的字段都是約定的,因為在native執(zhí)行初始化JS的時候,會用到,比如WVJBCallbacks。因為通信的過程是異步的(動態(tài)創(chuàng)建iframe,捕獲跳轉(zhuǎn))。所以setupWebViewJavascriptBridge是異步的,并且以后調(diào)用native提供的方法也會是異步的。并且最好是在setupWebViewJavascriptBridge的回調(diào)函數(shù)中調(diào)用,可以保證初始化成功了。而回調(diào)函數(shù)里面會把WebViewJavascriptBridge當(dāng)做參數(shù)返回。

上圖是網(wǎng)上偷懶截的圖,到時候底部放個鏈接。

重點:window.WebViewJavascriptBridge,所有的交互都是通過這個WebViewJavascriptBridge對象來完成的。初始化的過程就是動態(tài)創(chuàng)建一個iframe,將iframe的src設(shè)置為https://__bridge_loaded__,然后插入到頁面中。前面通信基礎(chǔ)說過,natvie能夠截獲h5跳轉(zhuǎn),當(dāng)Native捕獲到當(dāng)前URL,并且其值等于 https://__bridge_loaded__ (當(dāng)前URL是約定成俗的) ,就會注入一段自執(zhí)行的代碼(假設(shè)其為bridge,下面統(tǒng)一稱呼了),掛載WebViewJavascriptBridge到window上。等下會著重說下這段自執(zhí)行的代碼是如何工作的。

if (window.WebViewJavascriptBridge), if (window.WVJBCallbacks) ,保證了初始化只執(zhí)行一次。WVJBCallbacks這個字段用于還沒有初始化的時候,保存回調(diào)函數(shù)。當(dāng)我們通知native端進行初始化,并且初始化之后,bridge里面會去遍歷WVJBCallbacks中的回調(diào)函數(shù),并將WebViewJavascriptBridge當(dāng)做參數(shù)注入。執(zhí)行完后會delete window.WVJBCallbacks。

下圖是我們真正調(diào)用一個native的方法,假設(shè)去獲取用戶信息,WebViewJavascriptBridge是重點,下面會詳細(xì)講解下這個對象。

export function getUserData() {
  return new Promise((resolve, reject) => {
    setupWebViewJavascriptBridge((WebViewJavascriptBridge) => {
      WebViewJavascriptBridge.callHandler("xxxx", params, (data) => {
        resolve(data);
      });
    })
  })
}

到了這里,其實我們前端需要做的已經(jīng)完了。我再理一理順序。getUserData,去調(diào)用native提供的方法。先調(diào)用setupWebViewJavascriptBridge,里面有做是否初始化的判斷。然后回調(diào)函數(shù)里面,我們就能取到WebViewJavascriptBridge對象,對象上面掛載callHandler了方法,"xxxx"代表著和native端約定好的方法,執(zhí)行即可。

WebViewJavascriptBridge

最上面的時候說過,native可以執(zhí)行JS,能獲取到window。所以接下來重點講一下連接native和js的(bridge)橋梁是如何運作的。
什么時候初始化bridge?
重復(fù)說一下。setupWebViewJavascriptBridge第一次調(diào)用的時候,會創(chuàng)建一個iframe,src指向https://__bridge_loaded__。當(dāng)native截獲到這個請求的時候,判斷為bridge_loaded,就會注入一段自執(zhí)行的代碼進行WebViewJavascriptBridge初始化。
接下來看代碼:

    //如果已經(jīng)初始化了,則返回。
    if (window.WebViewJavascriptBridge) {
        return;
    }
    if (!window.onerror) {
        window.onerror = function(msg, url, line) {
            console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line);
        }
    }
    //初始化一些屬性。
    var messagingIframe;
    //用于存儲消息列表
    var sendMessageQueue = [];
    //用于存儲消息
    var messageHandlers = {};
    //通過下面兩個協(xié)議組合來確定是否是特定的消息,然后攔擊。
    var CUSTOM_PROTOCOL_SCHEME = "https";
    var QUEUE_HAS_MESSAGE = "__wvjb_queue_message__";
    //oc調(diào)用js的回調(diào)
    var responseCallbacks = {};
    //消息對應(yīng)的id
    var uniqueId = 1;
    //是否設(shè)置消息超時
    var dispatchMessagesWithTimeoutSafety = true;
    //web端注冊一個消息方法
    function registerHandler(handlerName, handler) {
        messageHandlers[handlerName] = handler;
    }
    //web端調(diào)用一個OC注冊的消息
    function callHandler(handlerName, data, responseCallback) {
        if (arguments.length == 2 && typeof data == "function") {
            responseCallback = data;
            data = null;
        }
        _doSend({ handlerName: handlerName, data: data }, responseCallback);
    }
    function disableJavscriptAlertBoxSafetyTimeout() {
        dispatchMessagesWithTimeoutSafety = false;
    }
        //把消息轉(zhuǎn)換成JSON字符串返回
    function _fetchQueue() {
        var messageQueueString = JSON.stringify(sendMessageQueue);
        sendMessageQueue = [];
        return messageQueueString;
    }
    //OC調(diào)用JS的入口方法
    function _handleMessageFromObjC(messageJSON) {
        _dispatchMessageFromObjC(messageJSON);
    }

    //初始化橋接對象,OC可以通過WebViewJavascriptBridge來調(diào)用JS里面的各種方法。
    window.WebViewJavascriptBridge = {
        registerHandler: registerHandler,
        callHandler: callHandler,
        disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
        _fetchQueue: _fetchQueue,
        _handleMessageFromObjC: _handleMessageFromObjC
    };


    //處理從OC返回的消息。
    function _dispatchMessageFromObjC(messageJSON) {
        if (dispatchMessagesWithTimeoutSafety) {
            setTimeout(_doDispatchMessageFromObjC);
        } else {
            _doDispatchMessageFromObjC();
        }

        function _doDispatchMessageFromObjC() {
            var message = JSON.parse(messageJSON);
            var messageHandler;
            var responseCallback;
            //回調(diào)
            if (message.responseId) {
                responseCallback = responseCallbacks[message.responseId];
                if (!responseCallback) {
                    return;
                }
                responseCallback(message.responseData);
                delete responseCallbacks[message.responseId];
            } else {//主動調(diào)用
                if (message.callbackId) {
                    var callbackResponseId = message.callbackId;
                    responseCallback = function(responseData) {
                        _doSend({ handlerName: message.handlerName, responseId: callbackResponseId, responseData: responseData });
                    };
                }
                //獲取JS注冊的函數(shù)
                var handler = messageHandlers[message.handlerName];
                if (!handler) {
                    console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
                } else {
                    //調(diào)用JS中的對應(yīng)函數(shù)處理
                    handler(message.data, responseCallback);
                }
            }
        }
    }
    //把消息從JS發(fā)送到OC,執(zhí)行具體的發(fā)送操作。
    function _doSend(message, responseCallback) {
        if (responseCallback) {
            var callbackId = "cb_" + (uniqueId++) + "_" + new Date().getTime();
            //存儲消息的回調(diào)ID
            responseCallbacks[callbackId] = responseCallback;
            //把消息對應(yīng)的回調(diào)ID和消息一起發(fā)送,以供消息返回以后使用。
            message["callbackId"] = callbackId;
        }
        //把消息放入消息列表
        sendMessageQueue.push(message);
        //下面這句話會出發(fā)JS對OC的調(diào)用
        //讓webview執(zhí)行跳轉(zhuǎn)操作,從而可以在
        //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler 中攔截到JS發(fā)給OC的消息
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + "://" + QUEUE_HAS_MESSAGE;
    }


    messagingIframe = document.createElement("iframe");
    messagingIframe.style.display = "none";
    //messagingIframe.body.style.backgroundColor="#0000ff";
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + "://" + QUEUE_HAS_MESSAGE;
    document.documentElement.appendChild(messagingIframe);


    //注冊_disableJavascriptAlertBoxSafetyTimeout方法,讓OC可以關(guān)閉回調(diào)超時,默認(rèn)是開啟的。
    registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
    //執(zhí)行_callWVJBCallbacks方法
    setTimeout(_callWVJBCallbacks, 0);

    //初始化WEB中注冊的方法。這個方法會把WEB中的hander注冊到bridge中。
    //下面的代碼其實就是執(zhí)行WEB中的callback函數(shù)。
    function _callWVJBCallbacks() {
        var callbacks = window.WVJBCallbacks;
        delete window.WVJBCallbacks;
        for (var i = 0; i < callbacks.length; i++) {
            callbacks[i](WebViewJavascriptBridge);
        }
    }
})();

這里著重講 JS如何調(diào)native方法的。
window.WebViewJavascriptBridge = {

    registerHandler: registerHandler,
    callHandler: callHandler,
    disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
    _fetchQueue: _fetchQueue,
    _handleMessageFromObjC: _handleMessageFromObjC
};

這個對象就是上文中回調(diào)函數(shù)獲取到的WebViewJavascriptBridge 對象。callHandler表示執(zhí)行某個約定的方法。
上面的源碼注釋很清晰了,源碼就不過多解讀了。接下來我們舉一個完整的列子來闡述整個過程。

重點
getUserData => 調(diào)用setupWebViewJavascriptBridge => 因為是第一次調(diào)用進行初始化,會將回調(diào)函數(shù)保存到WVJBCallbacks中。
=> 動態(tài)創(chuàng)建ifram,native截獲,注入代碼進行brige初始化 => WebViewJavascriptBridge初始化 => 第一次執(zhí)行所以會觸發(fā)
_callWVJBCallbacks,遍歷上面的WVJBCallbacks數(shù)組,并且將WebViewJavascriptBridge作為參數(shù)傳入,執(zhí)行回調(diào)。 => 回調(diào)中執(zhí)行的就是getUserData里面的WebViewJavascriptBridge.callHandler("xxxx")
=》 callHandler執(zhí)行的其實就是__doSend方法。 =》 _doSend里面會將getUserData里面的回調(diào)函數(shù)保存在全局對象變量responseCallbacks中,key則是自增的ID。并且把getUserData所調(diào)用的方法名,參數(shù),key,都放在一個對象中(message),并將這個
message,存到另外一個全局?jǐn)?shù)組變量sendMessageQueue中。 => 動態(tài)創(chuàng)建一個ifram,src為__wvjb_queue_message__,也就是說創(chuàng)建的ifram,一般就兩種地址,一個是告訴native進行初始化,一個是告訴native可以輪詢消息隊列sendMessageQueue了。 => native攔截到URL,遍歷全局變量sendMessageQueue,執(zhí)行g(shù)etUserData所需要的方法,這里就是XXXX,并組裝參數(shù) => 調(diào)用另一個_handleMessageFromObjC方法,解析參數(shù),得到一開始的自增ID,從全局responseCallbacks中取到真正的回調(diào)函數(shù)執(zhí)行。 => over了。完整的交互就是這個樣子的了。

上面的流程已經(jīng)很清楚了,如果有不懂,或者有錯誤的地方歡迎指正。
這是js到native端的消息過程,還有native調(diào)js的,其實過程差不多。參考下文把
WebViewJavascriptBridge原理解析

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109047.html

相關(guān)文章

  • iframe webview ,記錄一次使用 jsBridge 遇到的 bug 解決過程

    摘要:通訊狀態(tài)的改變次數(shù)被調(diào)用次數(shù)預(yù)計驗證證實完畢。既然是因為調(diào)用間隔太短,所以就采用了切圖仔常用的節(jié)流方案。隨便說說縱觀整個通訊過程,其實就是一個網(wǎng)絡(luò)協(xié)議的縮影。前提-出現(xiàn)場景 使用機型為 Android 9,API 28 使用的 jsBridge 為 link bug 描述 在頁面加載前后如果連續(xù)多次調(diào)用原生的方法,會遇到回調(diào)參數(shù)未被調(diào)用的情況。 // 多次調(diào)用如下函數(shù), 部分 callb...

    Jensen 評論0 收藏0
  • WebViewJavascriptBridge原理解析

    摘要:否則按照正常流程處理。如果是表示是初始化環(huán)境的消息,如果是則表示是發(fā)送消息。則立即發(fā)送消息?;卣{(diào)主動調(diào)用獲取注冊的函數(shù)調(diào)用中的對應(yīng)函數(shù)處理把消息從發(fā)送到,執(zhí)行具體的發(fā)送操作。處理從返回的消息。從而找到具體的實現(xiàn)執(zhí)行。 基本說明 我們的項目是一個OC與javascript重度交互的app,OC與javascript交互的那部分是在WebViewJavascriptBridge的githu...

    yck 評論0 收藏0
  • WebViewJavascriptBridge原理解析

    摘要:否則按照正常流程處理。如果是表示是初始化環(huán)境的消息,如果是則表示是發(fā)送消息。則立即發(fā)送消息?;卣{(diào)主動調(diào)用獲取注冊的函數(shù)調(diào)用中的對應(yīng)函數(shù)處理把消息從發(fā)送到,執(zhí)行具體的發(fā)送操作。處理從返回的消息。從而找到具體的實現(xiàn)執(zhí)行。 基本說明 我們的項目是一個OC與javascript重度交互的app,OC與javascript交互的那部分是在WebViewJavascriptBridge的githu...

    andot 評論0 收藏0
  • WebViewJavascriptBridge原理解析

    摘要:否則按照正常流程處理。如果是表示是初始化環(huán)境的消息,如果是則表示是發(fā)送消息。則立即發(fā)送消息?;卣{(diào)主動調(diào)用獲取注冊的函數(shù)調(diào)用中的對應(yīng)函數(shù)處理把消息從發(fā)送到,執(zhí)行具體的發(fā)送操作。處理從返回的消息。從而找到具體的實現(xiàn)執(zhí)行。 基本說明 我們的項目是一個OC與javascript重度交互的app,OC與javascript交互的那部分是在WebViewJavascriptBridge的githu...

    syoya 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<