關於Cordova框架對URL攔截導致通訊丟失問題的處理

窗前有月光發表於2018-12-04

前言

昨天看了味精大佬關於hybrid框架搭建的系列文章的從零收拾一個hybrid框架(一)-- 從選擇JS通訊方案開始,不得不說大佬總結的很細緻,尤其是基於WKWebView的總結很到位,文章裡面提到了UIWebView基於URL攔截的方式不安全問題,實際上之前我是沒有關注這個問題的,以至於後兩篇還沒來得及學習就陷入了沉思,那麼我一直使用的Cordova框架是怎麼處理這個問題的?還是說就沒有處理?關於這個問題,實際上Cordova框架內部是做了處理的,本篇主要針對Cordova對於URL攔截方式進行通訊做了哪些優化進行分析。

Cordova在iOS端是怎麼通訊的,我之前的文章淺析iOS-Cordova講的很清楚了,在iOS端主要是圍繞著一個queue陣列進行的,具體怎麼進行的本篇不做分析了,具體可以看前面提到的文章瞭解下。

本篇主要圍繞以下三點進行分析:

  • 1.js在給native傳送假請求的時候做了什麼
  • 2.實際上js是怎樣將各種引數傳遞給native的
  • 3.pokeNative優化了什麼

js在給native傳送假請求的時候做了什麼

真相都在cordova.js裡面,作為一個未入門的前端來說,對於cordova.js只能做一個簡要分析,主要是針對上面提到的三點,看程式碼。

function iOSExec() {
    //刪除了一些不在本篇討論範圍內的程式碼
    var command = [callbackId, service, action, actionArgs];
    commandQueue.push(JSON.stringify(command));
    if (!isInContextOfEvalJs && commandQueue.length == 1) {
        pokeNative();
    }
}
複製程式碼

在js端呼叫cordova外掛的時候,程式碼會走進cordova.js裡面的這個方法,push()函式實際上就是OC中的addObject:操作,commendQueue為cordova.js內維護的全域性陣列,commandQueue.push就是像commendQueue陣列的最後面新增了一個物件,也就是被轉為json格式的command物件。那麼實際上在前端連續多次頻繁的呼叫外掛的時候,外掛的command資訊都會被儲存在commandQueue中而不是把每一個通訊都要去做一個假請求。通過if (!isInContextOfEvalJs && commandQueue.length == 1)這個判斷可以看到如果commandQueue中的呼叫次數不為一,也就是說可能有多個的時候,是不會執行pokeNatie()的,pokeNative實際為傳送假請求的具體實現,後面會講到。從而也就避免了外掛被頻繁呼叫所引起的通訊丟失的情況。

那麼問題來了,外掛的呼叫都被儲存在了commandQueue中,native端怎麼獲取。這也是我們要討論的第二個問題。

實際上js是怎樣將各種引數傳遞給native的

那麼這個問題我們需要分析下另一個函式,看程式碼:

iOSExec.nativeFetchMessages = function() {
    if (failSafeTimerId) {
        clearTimeout(failSafeTimerId);
        failSafeTimerId = 0;
    }
    if (!commandQueue.length) {
        return '';
    }
    var json = '[' + commandQueue.join(',') + ']';
    commandQueue.length = 0;
    return json;
};
複製程式碼

這是native端收到cordova.js的pokeNative發出的假請求會呼叫的函式,這個json物件儲存的正是commandQueue中的外掛呼叫資訊,那麼不難看出,實際上Cordova內通訊引數並不是在URL上傳遞,而是JS端告訴Native過來取,大概意思就是我這有好多都取過去吧。實際上只pokeNative()了一次,那麼外掛呼叫就都被native端取走了,這樣也就避免了快速的頻繁的發假請求而導致通訊丟失問題。

pokeNative優化了什麼

實際上經過了上面兩步,還是不夠安全的,因為有一種情況,那就是當前端的第一次呼叫剛好結束的時候發生了第二次呼叫,這個時候已經執行了commandQueue.length = 0;函式,也就是說commandQueue已經被清空了,那麼就會執行pokeNative()函式,去發一個假請求給native端,這樣就導致了連續的兩次假請求發生。實際上這一塊cordova.js也是做了處理的,詳情在pokeNative()裡面,看程式碼:

function pokeNative() {
    //程式碼刪減部分
    failSafeTimerId = setTimeout(function() {
        if (commandQueue.length) {
            // CB-10106 - flush the queue on bridge change
            if (!handleBridgeChange()) {
                pokeNative();
             }
        }
    }, 50);
}
複製程式碼

setTimeout()函式在javaScript裡面相當於新增了個定時器,也就是在50毫秒之後再執行pokeNative()函式,這樣也就是做了一個50毫秒的間隔,從而避免了快速的兩次呼叫。這一塊pokeNative()函式內部程式碼註釋也有解釋,通訊效率會降低7%,但是畢竟這種情況不多,而且7%也在我們能接受的範圍內。

總結

基於URL攔截的方式,隨著JSCore和WKWebView的新特性的出現也正在被逐漸的取締,但是Cordova框架不單單給我們提供了一種通訊方式,更多的是它的設計思想,以及對hybrid框架的互動設計理念,如果有一天需要我們自己來做hybrid框架,我認為除了改變一下通訊方式以外,對於Cordova框架的其他部分都是很值得我們去學習的。

本文屬於原創,轉載註明出處。

相關文章