Hybrid App 應用開發中 5 個必備知識點複習

pingan8787發表於2019-06-26

前言

我們大前端團隊內部 ?每週一練 的知識複習計劃還在繼續,本週主題是 《Hybrid APP 混合應用專題》 ,這期內容比較多,篇幅也相對較長,每個知識點內容也比較多。

之前分享的每週內容,我都整理到掘金收藏集 ?《EFT每週一練》 上啦,歡迎點贊收藏咯??。

注:本文整理資料來源網路,有些圖片/段落找不到原文出處,如有侵權,聯絡刪除。

一、什麼是 Hybrid App,與 Native App 及 Web App 有什麼區別

參考文章:

  1. 《Web App Hybrid App和 Native App的區別》

  2. 《Hybrid APP基礎篇(二) -> Native、Hybrid、React Native、Web App方案的分析比較》

1.1 主流應用型別

隨著現在移動網際網路的快速發展,市面上目前主流移動應用程式主要分三類:Web AppNative AppHybrid App

三者大致關係如下:

關係圖

1.2 Web App

Web App,即移動端網站,一般指的是基於 Web 的應用,基於瀏覽器執行無需下載安裝,基本上可以說是觸屏版的網頁應用。這類應用基本上是一個網頁或一系列網頁,旨在在移動螢幕上工作。

Web 網站一般分為兩種:

  1. MPA(Multi-page Application)

  2. SPA(Single-page Application)

一般的 Web App 是指 SPA 形式開發的網站。

優點:

  • 開發和維護成本低,可以跨平臺,除錯方便;

前端人員開發的程式碼,可應用於各大主流瀏覽器(特殊情況可以程式碼進行下相容),沒有新的學習成本,而且可以直接在瀏覽器中除錯。

  • 更新最為快速;

由於web app資源是直接部署在伺服器端的,所以只需替換伺服器端檔案,使用者訪問是就已經更新了(當然需要解決一些快取問題)。

  • 無需安裝App,不會佔用手機記憶體;

通過瀏覽器即可訪問,無需安裝,使用者使用成本更低。

缺點:

  • 效能低,使用者體驗差;

由於是直接通過的瀏覽器訪問,所以無法使用原生的API,操作體驗不好。

  • 依賴於網路,頁面訪問速度慢,耗費流量;

Web App每次訪問都必須依賴網路,從服務端載入資源,當網速慢時訪問速度很不理想,特別是在移動端,對網站效能優化要求比較高。

  • 功能受限,大量功能無法實現;

只能使用 HTML5 的一些特殊 API ,無法呼叫原生 API ,所以很多功能存在無法實現情況。

  • 臨時性入口,使用者留存率低;

這既是它的優點,也是缺點,優點是無需安裝,確定是用完後有時候很難再找到,或者說很難專門為某個web app留存一個入口,導致使用者很難再次使用。

1.3 Native App

Native APP 指的是原生程式,需要使用者下載安裝使用,一般依託於作業系統,有很強的互動,是一個完整的App,可擴充性強,能釋出應用商店。

目前市面上主流的平臺有:AndroidiOS

優點:

  • 直接依託於作業系統,使用者體驗好操作流暢效能穩定

  • 使用者留存率高;

  • 功能最為強大,特別是在與系統互動中,幾乎所有功能都能實現;

由於 Native APP 是直接依託於系統,所以可以直接呼叫官方提供的API,功能最為全面(比如本地資源操作,通知,動畫等)。

缺點:

  • 開發和維護成本高,無法跨平臺,需要各平臺各自獨立開發;

Android 上基於 Java 開發,iOS 上基 OCSwift 開發,相互之間獨立,必須要有各自的開發人員。

  • 門檻較高,原生人員有一定的入門門檻,人才較少;

原生的一個很大特點就是獨立,所以不太容易入門,而且 AndroidiOS都需要獨立學習。

  • 分發成本高,更新緩慢,特別是釋出應用商店後,需要等到稽核週期;

原生應用更新是一個很大的問題, Android中還能直接下載整包APK進行更新,但是 iOS中,如果是釋出 AppStore ,必須通過 AppStore地址更新,而每次更新都需要稽核,所以無法達到及時更新。

1.4 Hybrid App

Hybrid App 指的是混合開發,也就是半原生半 Web 的開發模式,有跨平臺效果,當然了,實質最終釋出的仍然是獨立的原生APP(各種的平臺有各種的SDK)。

優點:

  • 學習和開發成本較低,可以跨平臺,除錯方便

Hybrid 開發模式下,由原生提供統一的 API 給 JS 呼叫,實際的主要邏輯由 HTML 和 JS 完成,最終放在 webview 中顯示,這樣只需要寫一套程式碼即可,達到跨平臺效果,另外也可以直接在瀏覽器中除錯,很方便。

一般 Hybrid 中的跨平臺最少可以跨三個平臺: Android App ,iOS App ,普通 webkit 瀏覽器。

需要前端人員關注一些原生提供的API,具體的實現無需關心,沒有新的學習內容。

  • 維護成本低,功能可複用,並且更容易更新;

雖然沒有 web app 更新那麼快速,但是 Hybrid 中也可以通過原生提供 api ,進行資源主動下載,達到只更新資原始檔,不更新 apk(ipa) 的效果。

  • 功能更加完善,效能和體驗要比起 web app 好太多;

因為可以呼叫原生api,所以很多功能只要原生提供出就可以實現,另外效能也比較接近原生。

  • 部分效能要求的頁面可用原生實現;

這種模式是原生混合 web ,所以我們完全可以將互動強,效能要求高的頁面用原生寫,然後一些其它頁面用 JS 寫,嵌入 webview 中,達到最佳體驗。

缺點:

  • 相比原生,效能仍然有較大損耗;

這種模式受限於 webview 的效能,相比原生而言有不少損耗,體驗無法和原生相比。

  • 不適用於互動性較強的app;

這種模式的主要適用:一些新聞閱讀類,資訊展示類的 app ,不適用於一些互動較強或者效能要求較高的 app (比如動畫較多就不適合)。

1.5 三者區別

三者使用場景對比:

對比圖

三者技術特徵對比:

對比圖

另外增加 ReactNative 一起放入作對比。

NativeApp WebApp HybridApp ReactNativeApp
原生功能體驗 優秀 良好 接近優秀
渲染效能 非常快 接近快
是否支援裝置底層訪問 支援 不支援 支援 支援
網路要求 支援離線 依賴網路 支援離線(資源存本地情況) 支援離線
更新複雜度 高(幾乎總是通過應用商店更新) 低(伺服器端直接更新) 較低(可以進行資源包更新) 較低(可以進行資源包更新)
程式語言 Android(Java),iOS(OC/Swift) js+html+css3 js+html+css3 主要使用JS編寫,語法規則JSX
社群資源 豐富(Android,iOS單獨學習) 豐富(大量前端資源) 有侷限(不同的Hybrid相互獨立) 豐富(統一的活躍社群)
上手難度 難(不同平臺需要單獨學習) 簡單(寫一次,支援不同平臺訪問) 簡單(寫一次,執行任何平臺) 中等(學習一次,寫任何平臺)
開發週期 較短 中等
開發成本 昂貴 便宜 較為便宜 中等
跨平臺 不跨平臺 所有H5瀏覽器 Android,iOS,h5瀏覽器 Android,iOS
APP釋出 AppStore Web伺服器 AppStore AppStore

1.6 三者如何選擇

這裡簡單介紹幾種情況,具體還是要以實際專案技術評估結果為主。

  • 選擇純 Native App 模式的情況:

效能要求極高,體驗要求極好,不追求開發效率

  • 選擇 Web App 模式的情況:

不追求使用者體驗和效能,對離線訪問沒要求,正常來說,如果追求效能和體驗,都不會選用web app。

  • 選擇 Hybrid App 模式的情況

大部分情況下的App都推薦採用這種模式,這種模式可以用原生來實現要求高的介面,對於一些比較通用型,展示型的頁面完全可以用web來實現,達到跨平臺效果,提升效率。一般好一點的Hybrid方案,都會把資源放在本地的,可以減少網路流量消耗

  • 選擇React Native App模式的情況

追求效能,體驗,同時追求開發效率,而且有一定的技術資本,捨得前期投入。

React Native這種模式學習成本較高,所以需要前期投入不少時間才能達到較好水平,但是有了一定水準後,開發起來它的優勢就體現出來了,效能不遜色原生,而且開發速度也很快

二、什麼是 Cordova,它的優缺點是什麼

參考文章: 《淺談Cordova框架》

2.1 Cordova 簡介

Cordova 是一個用基於 HTML、CSS 和 JavaScript 的,用於建立跨平臺移動應用程式的快速開發平臺。它使開發者能夠利用iPhone、Android、Palm、Symbian、WP7、Bada和Blackberry等智慧手機的核心功能——包括地理定位、加速器、聯絡人、聲音和振動等,此外 Cordova 擁有豐富的外掛,可以呼叫。

也可以用來開發原生和WebView元件之間的外掛介面

來源:
Cordova 是 PhoneGap 貢獻給 Apache 後的開源專案,是從 PhoneGap 中抽出的核心程式碼,是驅動 PhoneGap 的核心引擎。可以把它們的關係想象成類似於 Webkit 和 Google Chrome 的關係。

2.2 Cordova 架構圖

Cordova架構圖

架構圖介紹:

  • Web App

用於存放我們程式的程式碼,包括業務邏輯,還有一些執行需要的資源(如:CSS,JavaScript,圖片,媒體檔案等)。 應用的實現是通過 web 頁面,預設的本地檔名稱是 index.html ,應用執行在原生應用包裝的 WebView 中,這個原生應用是你分發到應用商店中的。

  • WebView

Cordova 用的 WebView 可以給應用提供完整使用者訪問介面,使得應用混合了 Webview 和原生的應用元件。

  • Cordova Plugins

外掛是 Cordova 生態系統的重要組成部分。它提供了 Cordova 和原生元件相互通訊的介面,並繫結到了標準的裝置API上,這使你能夠通過 JavaScript 呼叫原生程式碼

2.3 優缺點

優點:

  • 跨平臺,開發簡單,學習成本低;
  • 框架多,外掛多,可自定義外掛;
  • 發展最早,社群資源豐富;

缺點:

  • WebView效能低下時,使用者體驗差,反應慢;
  • 中文文件資源少;
  • 除錯不方便,既不像原生那麼好除錯,也不像純web那種除錯;

三、Cordova 外掛的原理是什麼

Cordova 外掛就是一些附加程式碼用來提供原生元件的 JavaScript 介面,它允許你的 App 可以使用原生裝置的能力,超越了純粹的 Web App。

Cordova 在 iOS 上的實現原理:

cordova

3.1 工作流程

  1. Cordova 發起對原生的請求:
cordova.exec(successCallback, failCallback, service, action, actionArgs); 
// successCallback: 成功回撥方法
// failCallback: 失敗回撥方法
// server: 所要請求的服務名字
// action: 所要請求的服務具體操作
// actionArgs: 請求操作所帶的引數
複製程式碼
  1. 這五個引數並不是直接傳給原生,Cordova JS 端會做以下處理:

    • 為每個請求生成一個唯一標識( callbackId ),並傳給原生端,原生端處理完後,會把 callbackId 連同處理結果一起返回給 JS 端;
    • callbackIdkey{success:successCallback, fail:failCallback}value,把這個鍵值對儲存在 JS 端的字典裡,successCallbackfailCallback 這兩個引數不需要傳給原生,原生返回結果時帶上callbackId,JS 端就可以根據 callbackId 找到回撥方法;
    • 每次 JS 請求,最後發到原生的資料包括:callbackId, service, action, actionArgs

iOS

  1. 原生程式碼拿到 callbackIdserviceactionactionArgs 後,會做以下處理:

    • 根據 service 引數找到對應外掛類;
    • 根據 action 引數找到外掛類中對應的處理方法,並把 actionArgs 作為處理方法請求引數的一部分傳給處理方法;
    • 處理完成後,把處理結果及 callbackId 返回給 JS 端,JS 端收到後會根據 callbackId 找到回撥方法,並把處理結果傳給回撥方法;

iOS

  1. JS 端根據 callbackId 回撥 cordova.js
// 根據 callbackId 及是否成功標識,找到回撥方法,並把處理結果傳給回撥方法
callbackFromNative: function(callbackId, success, status, args, keepCallback) {
    var callback = cordova.callbacks[callbackId];
    if (callback) {
        if (success && status == cordova.callbackStatus.OK) {
            callback.success && callback.success.apply(null, args);
        } else if (!success) {
            callback.fail && callback.fail.apply(null, args);
        }

        // Clear callback if not expecting any more results
        if (!keepCallback) {
            delete cordova.callbacks[callbackId];
        }
    }
}
複製程式碼

四、什麼是 JS Bridge,它的作用是什麼

參考文章:《JSBridge的原理》

4.1 JS Bridge 介紹

JSBridge 簡單來講,主要是 給 JavaScript 提供呼叫 Native 功能的介面,讓混合開發中的前端部分可以方便地使用地址位置、攝像頭甚至支付等 Native 功能。

JSBridge 就像其名稱中的 “Bridge” 的意義一樣,是 Native 和非 Native 之間的橋樑,它的核心是 構建 Native 和非 Native 間訊息通訊的通道,而且是 雙向通訊的通道

JSBridge 另一個叫法及大家熟知的 Hybrid app 技術。

brige

所謂 雙向通訊的通道:

  • JS 向 Native 傳送訊息 :

呼叫相關功能、通知 Native 當前 JS 的相關狀態等。

  • Native 向 JS 傳送訊息 :

回溯呼叫結果、訊息推送、通知 JS 當前 Native 的狀態等。

4.2. JS Bridge 實現原理

參考文章:《Hybrid APP基礎篇(四)->JSBridge的原理》

Android 和 iOS 的 JSBridge 實現方式:

brige原理

4.2.1 基本流程

brige原理

  • H5 頁面通過某種方式觸發一個 url scheme
  • Native 捕獲到 url scheme,並進行分析和處理;
  • Native 呼叫 H5 的 JSBridge 物件傳遞迴調;

原生的 WebView/UIWebView 控制元件已經能夠和 JS 實現資料通訊了,那為什麼還要 JSBridge呢?

其實使用JSBridge有很多方面的考慮:

  • Android4.2以下,addJavascriptInterface 方式有安全漏掉。
  • iOS7以下,JS 無法呼叫 Native。
  • url scheme 互動方式是一套現有的成熟方案,可以完美相容各種版本,對以前老版本技術的相容。
4.2.1 實現流程(Android 為例)

brige原理

  1. 擬定協議,參考 http 制定的協議為:jsbridge://className:port/methodName?jsonObj
className   // Android端實現暴露給前端的類名
port        // Android返回結果給前端的埠
methodName  // 前端需要呼叫的函式
jsonObj     // 前端給Android傳遞的引數
複製程式碼
  1. 新建 HTML 檔案命名為 index.html, 編寫一個 button 繫結 click 事件;
<button onclick="JSBridge.call(
    'bridge',
    'showToast',
    {'msg':'Hello JSBridge'},
    function(res){
        alert(JSON.stringify(res))
    }
)">
    測試showToast 
</button>
複製程式碼
  1. 新建 JS 檔案命名為 JSBridge.js, 第2步中的 JSBridge.call 即為呼叫 JSBridge.js中的 call 方法,後面帶了四個引數;
call: function (obj, method, params, callback) {
    console.log(obj+" "+method+" "+params+" "+callback);
    var port = Util.getPort();
    console.log(port);
    this.callbacks[port] = callback;
    var uri=Util.getUri(obj,method,params,port);
    console.log(uri);
    window.prompt(uri, "");
},
複製程式碼

JSBridge.js 中的 call 方法,最後呼叫了window.prompt 方法,這個方法就是觸發 Android 端 webChromeClient的回撥函式用的。

  1. window.prompt 觸發了 WebChromeClient(這個需要使用函式WebView.setWebChromeClient( new WebChromeClietn() )進行設定);

類中的如下回撥 onJsPrompt。這時就完成了前端與 Android端 的通訊了,因為前端的資訊都順利通過這個函式傳遞給Android了。

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
    result.confirm(JSBridge.callJava(view,message));
    return true;
}
複製程式碼
  1. Android 中會定義一個類 JSBridge.java 來管理暴露給前端使用的函式;

這個類有兩個功能:

  • 暴露給前端的函式的動態註冊功能。
  • 解析前端資訊,呼叫了 Android 端對應的函式,這個示例中是:showToast 函式。

解析前端的資訊,獲取前端呼叫的函式名:

Uri uri = Uri.parse(uriString);
className = uri.getHost();
param = uri.getQuery();
port = uri.getPort() + "";
String path = uri.getPath();
HashMap< String, Method> methodHashMap = exposedMethod.get(className);
Method method = methodHashMap.get(methodName);
複製程式碼

通過獲取的函式名,這裡是 showToast,呼叫 Android 端的 showToast函式。

method.invoke(null,webView,new JSONObject(param),new Callback(webView,port));
複製程式碼
  1. 定義類 BridgeImpl.java 來具體的實現暴露給前端的所有函式。這裡的 showToast函式如下:
public static void showToast(WebView webView, JSONObject param, final JSBridge.Callback callback){
    String message = param.optString("msg");
    Toast.makeText(webView.getContext(),message,Toast.LENGTH_LONG).show();
    if(null != callback){
        try {
            JSONObject object = new JSONObject();
            object.put("key","value");
            object.put("key1","vaule1");
            callback.apply(getJSONObject(0,"ok",object));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
複製程式碼

五、請列舉 Android 與 iOS 平臺下 JS Bridge 的實現方式

這邊程式碼比較多,我使用圖片來展示,大家可以放大來檢視。

5.1 Android 實現方式

5.1.1 Android 呼叫 JS 的 2 種方式
  1. 通過 WebView loadUrl():

JS 程式碼呼叫一定要在 onPageFinished() 回撥之後才能呼叫,否則不會呼叫。

Web 端程式碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>前端程式碼</title>
        <script>
        // Android需要呼叫的方法
        function callJS(){
            alert("Android呼叫了JS的callJS方法");
        }
    </script>
   </head>
</html>
複製程式碼

Android 端程式碼:

Android

  1. 通過 WebView evaluateJavascript()
// 只需要將第一種方法的loadUrl()換成下面該方法即可
mWebView.evaluateJavascript(
    "javascript:callJS()", 
    new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此處為 js 返回的結果
        }
    });
}
複製程式碼
5.1.2 JS 呼叫 Android 的 3 種方式
  1. 通過 WebViewaddJavascriptInterface() 進行物件對映:

Android 對映:

// 繼承自Object類
public class AndroidtoJs extends Object {
    // 定義JS需要呼叫的方法
    // 被JS呼叫的方法必須加入@JavascriptInterface註解
    @JavascriptInterface
    public void hello(String msg) {
        System.out.println("JS呼叫了Android的hello方法");
    }
}
複製程式碼

Web:

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <title>前端程式碼</title>  
      <script>
         function callAndroid(){
        // 由於物件對映,所以呼叫test物件等於呼叫Android對映的物件
            test.hello("js呼叫了android中的hello方法");
         }
      </script>
   </head>
   <body>
      //點選按鈕則呼叫callAndroid函式
      <button type="button" id="button1" "callAndroid()"></button>
   </body>
</html>
複製程式碼

Android 端:

Android

  1. 通過 WebViewClientshouldOverrideUrlLoading () 方法回撥攔截 url

Web 端:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>前端程式碼</title>
        <script>
            function callAndroid(){
                /*約定的url協議為:js://webview?arg1=111&arg2=222*/
                document.location = "js://webview?arg1=111&arg2=222";
            }
        </script>
    </head>
    <!-- 點選按鈕則呼叫callAndroid()方法  -->
    <body>
        <button type="button" id="button1" 
            onclick="callAndroid()"
        >點選呼叫Android程式碼</button>
    </body>
</html>
複製程式碼

Android 端:

Android

  1. 通過 WebChromeClient 的方法回撥攔截JS對話方塊方法:

通過 WebChromeClient 的 onJsAlert()onJsConfirm()onJsPrompt()方法回撥攔截JS對話方塊 alert()confirm()prompt() 訊息。

Web 端:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>前端程式碼</title>
        <script>
            function clickprompt(){
            // 呼叫prompt()
            var result=prompt("js://demo?arg1=111&arg2=222");
                alert("demo " + result);
            }
        </script>
    </head>
    <!-- 點選按鈕則呼叫clickprompt()  -->
    <body>
        <button type="button" id="button1" 
            onclick="clickprompt()"
        >點選呼叫Android程式碼</button>
    </body>
</html>
複製程式碼

Android 端:

Android

5.2 iOS 實現方式

5.2.1 JS 呼叫 iOS 的 2 種方式
  1. 使用 XMLHttpRequest 發起請求的方式:

Web 端:

iOS

XMLHttpRequest bridge:

JS 端使用 XMLHttpRequest 發起了一個請求:execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);,請求的地址是 /!gap_exec;並把請求的資料放在了請求的 header 裡面,見這句程式碼:execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());

而在 Objective-C 端使用一個 NSURLProtocol 的子類來檢查每個請求,如果地址是 /!gap_exec 的話,則認為是 Cordova 通訊的請求,直接攔截,攔截後就可以通過分析請求的資料,分發到不同的外掛類(CDVPlugin 類的子類)的方法中:

iOS

Cordova 中優先使用這種方式,Cordova.js 中的註釋有提及為什麼優先使用 XMLHttpRequest 的方式,及為什麼保留第二種 iframe bridge 的通訊方式:

// XHR mode does not work on iOS 4.2, so default to IFRAME_NAV for such devices.
// XHR mode’s main advantage is working around a bug in -webkit-scroll, which
// doesn’t exist in 4.X devices anyways123
複製程式碼

iframe bridge:

在 JS 端建立一個透明的 iframe,設定這個 ifamesrc 為自定義的協議,而 ifamesrc 更改時,UIWebView 會先回撥其 delegatewebView:shouldStartLoadWithRequest:navigationType: 方法,關鍵程式碼如下:

iOS

  1. 通過設定透明的 iframesrc 屬性:
5.2.2 iOS 呼叫 JS 的方式

UIWebView 有一個這樣的方法 stringByEvaluatingJavaScriptFromString:,這個方法可以讓一個 UIWebView 物件執行一段 JS 程式碼,這樣就可以達到 Objective-C 跟 JS 通訊的效果,在 Cordova 的程式碼中多處用到了這個方法,其中最重要的兩處如下:

  1. 獲取 JS 的請求資料:

iOS

  1. 把 JS 請求的結果返回給 JS 端:

iOS

結語

對於初入混合應用開發的小夥伴,這些會有點難度,但是好好理解下那幾張流程圖,再理一理思路,相信會有幫助?

給大家加加油~~

關於我

本文首發在 pingan8787個人部落格,如需轉載請保留個人介紹

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 github.com/pingan8787/…
ES小冊 js.pingan8787.com

微信公眾號

bg

相關文章