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

前言

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

以前分享的每週內容,我都整理到掘金收藏集 [?《EFT每週一練》] (https://juejin.im/collection/5cd11b0af265da0346227e24) 上啦,歡迎點贊收藏咯??。html

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

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

參考文章:java


  1. [《Web App Hybrid App和 Native App的區別》] (http://www.ionic.wang/article-index-id-58.html)android



  2. [《Hybrid APP基礎篇(二) -> Native、Hybrid、React Native、Web App方案的分析比較》] (https://www.cnblogs.com/dailc/p/5930238.html)web


1.1 主流應用類型

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

三者大體關係以下:api

640?wx_fmt=png

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 三者區別

三者使用場景對比:

640?wx_fmt=png

三者技術特徵對比:

640?wx_fmt=png

另外增長 ReactNative 一塊兒放入做對比。

640?wx_fmt=png


1.6 三者如何選擇

這裏簡單介紹幾種狀況,具體仍是要以實際項目技術評估結果爲主。

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

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

  • 選擇 Web App 模式的狀況:

不追求用戶體驗和性能,對離線訪問沒要求,正常來講,若是追求性能和體驗,都不會選用web app。

  • 選擇 Hybrid App 模式的狀況

大部分狀況下的App都推薦採用這種模式,這種模式能夠用原生來實現要求高的界面,對於一些比較通用型,展現型的頁面徹底能夠用web來實現,達到跨平臺效果,提高效率。通常好一點的Hybrid方案,都會把資源放在本地的,能夠減小網絡流量消耗

  • 選擇React Native App模式的狀況

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

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

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

參考文章:[《淺談Cordova框架》] (https://blog.csdn.net/weixin_37730482/article/details/73920722)

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 架構圖

640?wx_fmt=jpeg

架構圖介紹:

  • 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 上的實現原理:640?wx_fmt=png

3.1 工做流程

  1. Cordova 發起對原生的請求:

cordova.exec(successCallback, failCallback, service, action, actionArgs); 	
// successCallback: 成功回調方法	
// failCallback: 失敗回調方法	
// server: 所要請求的服務名字	
// action: 所要請求的服務具體操做	
// actionArgs: 請求操做所帶的參數

  1. 這五個參數並非直接傳給原生,Cordova JS 端會作如下處理:


  • 爲每一個請求生成一個惟一標識( callbackId ),並傳給原生端,原生端處理完後,會把 callbackId 連同處理結果一塊兒返回給 JS 端;

  • 以 callbackId 爲 key, {success:successCallback,fail:failCallback} 爲 value,把這個鍵值對保存在 JS 端的字典裏, successCallback 與 failCallback 這兩個參數不須要傳給原生,原生返回結果時帶上 callbackId,JS 端就能夠根據 callbackId 找到回調方法;

  • 每次 JS 請求,最後發到原生的數據包括: callbackIdserviceactionactionArgs

640?wx_fmt=png


  1. 原生代碼拿到 callbackIdserviceactionactionArgs 後,會作如下處理:


  • 根據 service 參數找到對應插件類;

  • 根據 action 參數找到插件類中對應的處理方法,並把 actionArgs 做爲處理方法請求參數的一部分傳給處理方法;

  • 處理完成後,把處理結果及 callbackId 返回給 JS 端,JS 端收到後會根據 callbackId 找到回調方法,並把處理結果傳給回調方法;

640?wx_fmt=png

  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的原理》] (https://juejin.im/post/5abca877f265da238155b6bc)

4.1 JS Bridge 介紹

JSBridge 簡單來說,主要是 給 JavaScript 提供調用 Native 功能的接口,讓混合開發中的前端部分能夠方便地使用地址位置、攝像頭甚至支付等 Native 功能。

JSBridge 就像其名稱中的 「Bridge」 的意義同樣,是 Native 和非 Native 之間的橋樑,它的核心是 構建 Native 和非 Native 間消息通訊的通道,並且是 雙向通訊的通道

JSBridge 另外一個叫法及你們熟知的 Hybrid app 技術。

640?wx_fmt=png

所謂 雙向通訊的通道:

  • JS 向 Native 發送消息 :

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

  • Native 向 JS 發送消息 :

回溯調用結果、消息推送、通知 JS 當前 Native 的狀態等。

4.2. JS Bridge 實現原理

參考文章:[《Hybrid APP基礎篇(四)->JSBridge的原理》] (https://www.cnblogs.com/dailc/p/5931324.html)

Android 和 iOS 的 JSBridge 實現方式:

640?wx_fmt=jpeg

4.2.1 基本流程

640?wx_fmt=png

  • 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 爲例)

640?wx_fmt=png

  1. 擬定協議,參考 http 制定的協議爲: jsbridge://className:port/methodName?jsonObj

className   // Android端實現暴露給前端的類名	
port        // Android返回結果給前端的端口	
methodName  // 前端須要調用的函數	
jsonObj     // 前端給Android傳遞的參數
  1. 新建 HTML 文件命名爲 index.html, 編寫一個 button 綁定 click 事件;

<button οnclick="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(newWebChromeClietn() )進行設定);

類中的以下回調 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 端代碼:

640?wx_fmt=png

  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. 經過 WebView 的 addJavascriptInterface() 進行對象映射:

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 端:

640?wx_fmt=png

  1. 經過 WebViewClient 的 shouldOverrideUrlLoading() 方法回調攔截 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" 	
            οnclick="callAndroid()"	
        >點擊調用Android代碼</button>	
    </body>	
</html>

Android 端:

640?wx_fmt=png

  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" 	
            οnclick="clickprompt()"	
        >點擊調用Android代碼</button>	
    </body>	
</html>

Android 端:

640?wx_fmt=png

5.2 iOS 實現方式

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

Web 端:

640?wx_fmt=png

XMLHttpRequest bridge:

JS 端使用 XMLHttpRequest 發起了一個請求:execXhr.open('HEAD',"/!gap_exec?"+(+newDate()),true);,請求的地址是 /!gap_exec;並把請求的數據放在了請求的 header 裏面,見這句代碼:execXhr.setRequestHeader('cmds',iOSExec.nativeFetchMessages());

而在 Objective-C 端使用一個 NSURLProtocol 的子類來檢查每一個請求,若是地址是 /!gap_exec 的話,則認爲是 Cordova 通訊的請求,直接攔截,攔截後就能夠經過分析請求的數據,分發到不一樣的插件類(CDVPlugin 類的子類)的方法中:

640?wx_fmt=png

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: 方法,關鍵代碼以下:

640?wx_fmt=png

  1. 經過設置透明的 iframe 的 src 屬性:

5.2.2 iOS 調用 JS 的方式

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

  1. 獲取 JS 的請求數據:

640?wx_fmt=png

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

640?wx_fmt=png

結語

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

給你們加加油~~

關於我

本文首發在 [pingan8787我的博客] (http://www.pingan8787.com),如需轉載請保留我的介紹

微信公衆號

640?wx_fmt=png

相關文章
相關標籤/搜索