1. 程式人生 > >【Ionic】Ionic實現iOS與Android端程式碼『熱更新』與Android升級下載功能 ( v1.3.x版本 )

【Ionic】Ionic實現iOS與Android端程式碼『熱更新』與Android升級下載功能 ( v1.3.x版本 )

熱更新的好處
通常ionic原始碼可包括(HTML,JavaScript,CSS檔案和其他資源),往常我們必須通過提交程式到應用市場,經過漫長的稽核後才可讓使用者更新,每改動一個小地方都需要重新打新版本。

現在ionic通過使用cordova外掛cordova-hot-code-push實現實現iOS與Android端程式碼『熱更新』功能,可不必釋出應用市場經平臺稽核,便可動態更新App原始碼的目的。

熱更新實現原理
這裡寫圖片描述

基礎實現方法
1、安裝 cordova-hot-code-push-cli

使用命令安裝

npm install -g cordova-hot-code-push
-cli

主要是生成檢測配置檔案,通常是在 www 目錄下動態生成 chcp.json 和 chcp.manifest 生成兩個檔案,外掛原始碼地址:

2、建立專案

包含 www 目錄的專案,已有專案無需重新建立

3、安裝熱更新外掛

使用命令安裝

ionic plugin add cordova-hot-code-push-plugin
ionic plugin add cordova-hot-code-push-local-dev-addon

4、打包封裝

執行命令cordova-hcp build 或者 cordova build

5、啟動 hcp server 服務

重新開啟一個終端視窗,cd到專案目錄(包含www目錄的父級目錄)執行命令

cordova-hcp server

稍等會在 www 目錄下動態生成 chcp.json 和 chcp.manifest 生成兩個檔案

6、正常在執行完成前4步以後會在我們config.xml動態加入如圖所示連結地址

這裡寫圖片描述

這裡寫圖片描述

7、執行程式碼或打包我們的App

8、修改我們想要更新的程式碼

然後修改 chcp.json 檔案的 content_url ,此地址為我們專案放置的地址

{
  "autogenerated": true,
  "release": "2016.07.17-11.36.13"
, "content_url": "http://kaibin.me/hotcode", "update": "now" }

9、將專案www目錄程式碼上傳到伺服器可訪問的目錄裡

例如在伺服器根目錄建立hotcode命名的目錄將專案www裡的檔案上傳上去

10、 關閉我們的應用重新開啟,看看程式碼是否更新成功

**優化流程
1.建立cordova-hcp模板**

線上測試可解除安裝掉 cordova-hot-code-push-local-dev-addon防止每次自動更新新版本,可通過命令解除安裝:

cordova plugin remove cordova-hot-code-push-local-dev-addon

可以在 cordova 專案根目錄下放一個 cordova-hcp.json,這是個模板檔案
這樣每次執行

cordova-hcp build

就會利用這個模板生成新的 chcp.json,而不用手動更改 www/chcp.json了。
cordova-hcp.json內容如下:

{
  "autogenerated": true,
  "content_url": "http://kaibin.me/hotcode",
  "min_native_interface": 1, // app核心版本號
  "ios_identifier": "https://itunes.apple.com/cn/app/***", // iOS上線後的地址,用於核心版本更新後的確認跳轉
  "update": "now"
}

2.Build options build設定,配置開發環境、測試環境與生產環境

在 /Cordova/Testproject/ 下建立 chcpbuild.options 檔案,檔案內容如下:

{
  "dev": {
    "config-file": "https://dev.kaibin.me/hotcode/www/chcp.json"
  },
  "production": {
    "config-file": "https://kaibin.me/hotcode/chcp.json"
  },
  "QA": {
    "config-file": "https://test.kaibin.me/hotcode/chcp.json"
  }
}

這樣在build app的時候, 轉為開發要用的伺服器, 可執行:

ionic build -- chcp-dev

結果就是, 特定拍下的 config.xml 檔案(比如, /Cordova/TestProject/platforms/android/res/xml/config.xml) 變成了這樣:

<chcp>
  <config-file url="https://dev.kaibin.me/hotcode/chcp.json"/>
</chcp>

當我們需要上架app的時候 (Google Play, App Store) - 我們正常build:

ionic build --release

這樣 config.xml 不會改變

如果沒有使用 chcpbuild.options 外掛會使用 config.xml 裡面預設的值。

檔案必須位於 Cordova 專案根目錄. 在這個檔案裡面,指定(JSON格式) 所有想改變 config.xml 檔案的配置,原始檔 config.xml (Cordova專案根目錄) 不會發生變動, 改變的是 特定平臺下的 config.xml (在cordova build過程的 after_prepare 階段)。

通過min_native_interface監控app是否提示更新

所需最小的外殼app版本. 這是app的build版本號,是個整型數字, 不是應用商店中看到的形如”1.0.0”字串。

在 config.xml中,這樣指定build版本號:

<chcp>
    <native-interface version="1"/>
    <config-file url="http://kaibin.me/hotcode/chcp.json"/>
</chcp>

與www目錄下的chcp.json裡面的min_native_interface數值相對應

例如:app外殼裡的config.xml是這樣的:

<chcp>
    <native-interface version="1"/>
    <config-file url="http://kaibin.me/hotcode/chcp.json"/>
</chcp>

若伺服器裡的min_native_interface也對應是1,不會出現提示使用者升級的狀態,正常修改www目錄的內容通過更新release值,可實現熱更新。

{ 
 "autogenerated": true,
 "content_url": "http://kaibin.me/hotcode", 
 "min_native_interface": 1, // app核心版本號
 "ios_identifier": "https://itunes.apple.com/cn/app/***", // iOS上線後的地址,用於核心版本更新後的確認跳轉
 "release": "2017.07.17-12.22.11",
 "update": "now"
}

假設你的外殼app加了個新的外掛,應該會更新外殼app。為了防止使用者通過熱更新下載了不適合他現有外殼app的web內容,你應該設定 min_native_interface 這個值

{ 
 "autogenerated": true,
 "content_url": "http://kaibin.me/hotcode",
 "min_native_interface": 1, // app核心版本號
 "ios_identifier": "https://itunes.apple.com/cn/app/***", // iOS上線後的地址,用於核心版本更新後的確認跳轉 
 "release": "2017.07.17-12.28.21",
 "min_native_interface": 2"update": "now"
}

外掛載入到這段json的時候, 發現 min_native_interface 比當前外殼app的build號要大,便不會下載web內容。而是觸發一個chcp_updateLoadFailed 錯誤通知, 告訴使用者需要升級外殼app了。

chcp.json中update欄位含義

指定了什麼時候安裝web內容更新,支援的值有:

start - app啟動時安裝更新,預設值
resume - app從後臺切換過來的時候安裝更新
now - web內容下載完畢即安裝更新

引導使用者去應用商店更新外殼app或下載新版本
通過給web更新設定最小支援的外殼app版本 min_native_interface。 如果外掛檢查發現使用者安裝的外殼app版本比服務端新的web內容要求的版本要低,就會觸發錯誤事件,錯誤碼:

chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW

通過這個錯誤碼可通過彈窗提示使用者去升級,跳轉到AppStore或下載新安裝包(國內因GFW,跳轉到google應用商店就算了= =)

chcp.json 裡增加min_native_interface的值

js端監聽相應事件,並在出現錯誤的時候呼叫 chcp.requestApplicationUpdate 方法

cordova-plugin-file,cordova-plugin-file-transfer,cordova-plugin-file-opener2先把這幾個外掛安裝好

var appUpdate = {
        // Application Constructor
        initialize: function() {
            this.bindEvents();
        },
        // Bind any events that are required.
        // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
        bindEvents: function() {
            document.addEventListener('deviceready', this.onDeviceReady, false);
            document.addEventListener('chcp_updateLoadFailed', this.onUpdateLoadError, false);
        },
        // deviceready Event Handler
        onDeviceReady: function() {
        },
        onUpdateLoadError: function(eventData) {
            var error = eventData.detail.error;

            // 當檢測出核心版本過小
            if (error && error.code == chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW) {
                var dialogMessage = '有新的版本,請下載更新';

                // iOS端 直接彈窗提示升級,點選ok後自動跳轉
                if(ionic.Platform.isIOS()){
                    chcp.requestApplicationUpdate(dialogMessage, this.userWentToStoreCallback, this.userDeclinedRedirectCallback);
                // Android端 提示升級下載最新APK檔案
                }else if(ionic.Platform.isAndroid()){
                    var confirmPopup = $ionicPopup.confirm({
                        template: '有新的版本,請下載更新',
                        cssClass: 'popup',
                        cancelText:'取消',
                        okText:'升級'
                    });
                    confirmPopup.then(function (res) {
                        if (res) {
                            $ionicLoading.show({
                                template: "已經下載:0%"
                            });
                            window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, function(fileEntry) {
                                fileEntry.getDirectory("***(app名稱)", { create: true, exclusive: false }, function (fileEntry) {
                                    //下載程式碼
                                    var fileTransfer = new FileTransfer();
                                    fileTransfer.download("app下載地址", fileEntry.toInternalURL()+"***(app名稱).apk", function(entry) {
                                        // 開啟下載下來的APP
                                        cordova.plugins.fileOpener2.open(
                                            entry.toInternalURL(),//下載檔案儲存地址
                                            'application/vnd.android.package-archive', {//以APK檔案方式開啟
                                                error: function(err) {
                                                },
                                                success: function() {}
                                            });
                                    }, function(err) {
                                    },true);
                                    fileTransfer.onprogress = function(progressEvent) {
                                        $timeout(function () {
                                            var downloadProgress = (progressEvent.loaded / progressEvent.total) * 100;
                                            $ionicLoading.show({
                                                template: "已經下載:" + Math.floor(downloadProgress) + "%"
                                            });
                                            if (downloadProgress > 99) {
                                                $ionicLoading.hide();
                                            }
                                        });
                                    };
                                },function(err){alert("建立失敗")});
                            });
                        }
                    });
                }
            }
        },
        userWentToStoreCallback: function() {
            // user went to the store from the dialog
        },
        userDeclinedRedirectCallback: function() {
            // User didn't want to leave the app.
            // Maybe he will update later.
        }
    };
    appUpdate.initialize();

iOS與Android的出現的問題
在build ios的app時config.xml的

<name>***</name>

name不可使用中文,設定app名稱為中文,可通過Xcode修改Resources目錄下的*-Info.plist內的Bundle display name欄位即可