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

封面

前言

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

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

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

1、什麼是 Hybird App,與 Native App 及 Web App 有什麼區別

參考文章:前端

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

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

1.1 主流應用類型

隨着如今移動互聯網的快速發展,市面上目前主流移動應用程序主要分三類:Web AppNative AppHybrid Appcss3

三者大體關係以下:git

關係圖

1.2 Web App

Web App,即移動端網站,通常指的是基於 Web 的應用,基於瀏覽器運行無需下載安裝,基本上能夠說是觸屏版的網頁應用。這類應用基本上是一個網頁或一系列網頁,旨在在移動屏幕上工做。github

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這種模式學習成本較高,因此須要前期投入很多時間才能達到較好水平,可是有了必定水準後,開發起來它的優點就體現出來了,性能不遜色原生,並且開發速度也很快

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

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

2.1 Cordova 簡介

Cordova 是一個用基於 HTML、CSS 和 JavaScript 的,用於建立跨平臺移動應用程序的快速開發平臺。它使開發者可以利用iPhone、Android、Palm、Symbian、WP七、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那種調試;

3、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];
        }
    }
}

4、什麼是 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();
        }
    }
}

5、請列舉 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
每日文章推薦 https://github.com/pingan8787/Leo_Reading/issues
ES小冊 js.pingan8787.com

微信公衆號

bg

相關文章
相關標籤/搜索