cordova+codepush 热更新实践 (微软 cordova-plugin-code-push)

系统环境 windows 10
所需工具 node.js, android studio, 安装方法自行百度/谷歌

cordova-hot-code-push 不再维护, 转而使用 cordova-plugin-code-push


安装 codepush

npm install -g code-push-cli

创建一个 CodePush 的云账户

code-push register

浏览器会自动打开窗口进行注册, 可用 github 帐号登录, 登录认证完毕会给一个key, 在把这个 key 粘贴到刚才命令行中, 回车注册完毕

创建 CodePush 应用

假设创建一个 test 安卓应用 (使用命令 code-push app add <appName> <os> <platform>)

code-push app add test android cordova

创建后生成对应 key, Production keyStaging key 分别为 生产环境key开发环境key

┌────────────┬──────────────────────────────────────┐
│ Name       │ Deployment Key                       │
├────────────┼──────────────────────────────────────┤
│ Production │ ********** Production key ********** │
├────────────┼──────────────────────────────────────┤
│ Staging    │ ********** Staging key ************* │
└────────────┴──────────────────────────────────────┘

*忘记的话可执行命令 code-push deployment ls <appName> -k 查看


安装 cordova

npm install -g cordova

创建 cordova 应用

cordova create helloworld com.example.hello HelloWorld

helloworld: 项目文件夹名
com.example.hello: 项目包名
HelloWorld: 项目名

添加应用平台

cd helloworld
cordova platform add android

添加插件

cordova plugin add cordova-plugin-code-push@latest
cordova plugin add cordova-plugin-whitelist

修改 config.xml, 添加 codepush 生成的 key

<platform name="android">
    <preference name="CodePushDeploymentKey" value="********** Staging key or Production key *************" />
</platform>

修改 config.xml, 允许与CodePush服务器通信

<access origin="*" />

<access origin="https://codepush.azurewebsites.net" />
<access origin="https://codepush.blob.core.windows.net" />
<access origin="https://codepushupdates.azureedge.net" />

修改 www/index.html, 添加 meta

<meta http-equiv="Content-Security-Policy" content="default-src https://codepush.azurewebsites.net 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *" />

修改 www/js/index.js
(js具体使用方法参考文档: https://github.com/Microsoft/cordova-plugin-code-push)

使用场景为打开应用有更新则弹窗提示
简单版:

window.document.addEventListener('deviceready', function () {

    // reference: https://github.com/Microsoft/cordova-plugin-code-push#syncoptions
    window.codePush.sync(syncStatus, {
        updateDialog: true,
        installMode: InstallMode.IMMEDIATE,
        updateDialog: {
            updateTitle: "An update is available!",
            optionalUpdateMessage: "Message",
            optionalInstallButtonLabel: "Install Button",
            optionalIgnoreButtonLabel: "Ignore Button",
        }
    }, onProgress);
    
    // reference: https://github.com/Microsoft/cordova-plugin-code-push#syncstatus
    function syncStatus(status) {
        switch (status) {
            case SyncStatus.UP_TO_DATE:
                console.log("UP_TO_DATE");
                break;
            case SyncStatus.UPDATE_INSTALLED:
                console.log("UPDATE_INSTALLED");
                break;
            case SyncStatus.UPDATE_IGNORED:
                console.log("UPDATE_IGNORED");
                break;
            case SyncStatus.IN_PROGRESS:
                console.log("IN_PROGRESS");
                break;
            case SyncStatus.CHECKING_FOR_UPDATE:
                console.log("CHECKING_FOR_UPDATE");
                break;
            case SyncStatus.AWAITING_USER_ACTION:
                console.log("AWAITING_USER_ACTION");
                break;
            case SyncStatus.DOWNLOADING_PACKAGE:
                console.log("DOWNLOADING_PACKAGE");
                break;
            case SyncStatus.INSTALLING_UPDATE:
                console.log("INSTALLING_UPDATE");
                break;
            case SyncStatus.ERROR:
                console.log("ERROR");
                break;
        }
    } 

    function onProgress(downloadProgress) {
        console.log("Downloading " + downloadProgress.receivedBytes + " of " + downloadProgress.totalBytes + " bytes.");
    };
})

复杂版:

var app = {

    initialize: function () {
        this.bindEvents();
    },
    bindEvents: function () {
        window.document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function() {
        // reference: https://github.com/Microsoft/cordova-plugin-code-push#syncoptions
        window.codePush.sync(app.syncStatus, {
            updateDialog: true,
            installMode: InstallMode.IMMEDIATE,
            updateDialog: {
                updateTitle: "An update is available!",
                optionalUpdateMessage: "Message",
                optionalInstallButtonLabel: "Install Button",
                optionalIgnoreButtonLabel: "Ignore Button",
            }
        }, app.downloadProgress);
    },
    // reference: https://github.com/Microsoft/cordova-plugin-code-push#syncstatus
    syncStatus: function(status) {
        switch (status) {
            case SyncStatus.UP_TO_DATE:
                console.log("UP_TO_DATE");
                break;
            case SyncStatus.UPDATE_INSTALLED:
                console.log("UPDATE_INSTALLED");
                break;
            case SyncStatus.UPDATE_IGNORED:
                console.log("UPDATE_IGNORED");
                break;
            case SyncStatus.IN_PROGRESS:
                console.log("IN_PROGRESS");
                break;
            case SyncStatus.CHECKING_FOR_UPDATE:
                console.log("CHECKING_FOR_UPDATE");
                break;
            case SyncStatus.AWAITING_USER_ACTION:
                console.log("AWAITING_USER_ACTION");
                break;
            case SyncStatus.DOWNLOADING_PACKAGE:
                console.log("DOWNLOADING_PACKAGE");
                break;
            case SyncStatus.INSTALLING_UPDATE:
                console.log("INSTALLING_UPDATE");
                break;
            case SyncStatus.ERROR:
                console.log("ERROR");
                break;
        }
    },
    downloadProgress: function(downloadProgress) {
        console.log("Downloading " + downloadProgress.receivedBytes + " of " + downloadProgress.totalBytes + " bytes.");
    },
};

app.initialize();

询问CodePush服务配置的应用程序部署是否有可用的更新, 场景为点击 '检查更新' 按钮触发事件
简单版:

window.document.addEventListener('deviceready', function () {
    
    console.log('deviceready');

    window.codePush.notifyApplicationReady(onNotifySucceeded, onNotifyFailed);
    window.codePush.checkForUpdate(onUpdateCheck, onError);
    
    function onError(error) {
        console.log("An error occurred. " + error);
    };
    
    function onInstallSuccess() {
        console.log("Installation succeeded.");
    };
    
    function onNotifySucceeded() {
        console.log("NotifySucceeded.");
    };
    
    function onNotifyFailed(error) {
        console.log("NotifyFailed. " + error);
    }

    function onPackageDownloaded(localPackage) {
        console.log("Package downloaded at: " + localPackage.localPath);
        console.log("localPackage appVersion: " + localPackage.appVersion);
        console.log("localPackage description: " + localPackage.description);
        console.log("localPackage failedInstall: " + localPackage.failedInstall);
        console.log("localPackage isFirstRun: " + localPackage.isFirstRun);
        console.log("localPackage isMandatory: " + localPackage.isMandatory);
        // InstallMode.IMMEDIATE: 立即更新APP
        // InstallMode.ON_NEXT_RESTART: 到下一次启动应用时更新
        // InstallMode.ON_NEXT_RESUME: 当应用从后台返回时更新
        localPackage.install(onInstallSuccess, onError, { installMode: InstallMode.ON_NEXT_RESUME, mandatoryInstallMode: InstallMode.ON_NEXT_RESTART });
    };

    function onProgress(downloadProgress) {
        console.log("Downloading " + downloadProgress.receivedBytes + " of " + downloadProgress.totalBytes + " bytes.");
    };

    function onUpdateCheck(remotePackage) {
        if (!remotePackage) {
            console.log("The application is up to date.");
        } else {
            // The hash of each previously reverted package is stored for later use. 
            // This way, we avoid going into an infinite bad update/revert loop.
            if (!remotePackage.failedInstall) {
                console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage));
                console.log("A CodePush update is available. Package hash: " + remotePackage.packageHash);
                remotePackage.download(onPackageDownloaded, onError, onProgress);
            } else {
                console.log("The available update was attempted before and failed.");
            }
        }
    };
})

复杂版:

var app = {

    initialize: function () {
        this.bindEvents();
    },
    bindEvents: function () {
        window.document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function() {
        app.notifyApplicationReadys();
        app.checkForUpdates();
    },
    notifyApplicationReadys: function() {
        var onNotifySucceeded = function() {
            console.log("NotifySucceeded.");
        };
        var onNotifyFailed = function(error) {
            console.log("NotifyFailed. " + error);
        };
        window.codePush.notifyApplicationReady(onNotifySucceeded, onNotifyFailed);
    },
    checkForUpdates: function() {
        var onInstallSuccess = function() {
            console.log("Installation succeeded.");
        };
        var onUpdateCheck = function(remotePackage) {
            if (!remotePackage) {
                console.log("The application is up to date.");
            } else {
                // The hash of each previously reverted package is stored for later use.
                // This way, we avoid going into an infinite bad update/revert loop.
                if (!remotePackage.failedInstall) {
                    console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage));
                    console.log("A CodePush update is available. Package hash: " + remotePackage.packageHash);
                    remotePackage.download(onPackageDownloaded, app.onError, onProgress);
                } else {
                    console.log("The available update was attempted before and failed.");
                }
            }
        };
        var onPackageDownloaded = function(localPackage) {
            console.log("Package downloaded at: " + localPackage.localPath);
            console.log("localPackage appVersion: " + localPackage.appVersion);
            console.log("localPackage description: " + localPackage.description);
            console.log("localPackage failedInstall: " + localPackage.failedInstall);
            console.log("localPackage isFirstRun: " + localPackage.isFirstRun);
            console.log("localPackage isMandatory: " + localPackage.isMandatory);
            console.log("localPackage packageSize: " + localPackage.packageSize);
            // InstallMode.IMMEDIATE: 立即更新APP
            // InstallMode.ON_NEXT_RESTART: 到下一次启动应用时更新
            // InstallMode.ON_NEXT_RESUME: 当应用从后台返回时更新
            localPackage.install(onInstallSuccess, app.onError, { installMode: InstallMode.ON_NEXT_RESUME, mandatoryInstallMode: InstallMode.ON_NEXT_RESTART });
        };

        var onProgress = function(downloadProgress) {
            console.log("Downloading " + downloadProgress.receivedBytes + " of " + downloadProgress.totalBytes + " bytes.");
        };
        window.codePush.checkForUpdate(onUpdateCheck, app.onError);
    },
    onError: function(error) {
        console.log("An error occurred. " + error);
    },
};

app.initialize();

编译产出 apk, 产出路径 platforms/android/app/build/outputs/apk/debug/app-debug.apk

cordova build

手机安装产出的 apk 应用
或者使用 android studio 导入 platforms/android 代码, 添加一个 Virtual Device 进行测试 (推荐)


发布更新

code-push release-cordova test android -d "Staging" --des "描述"
Usage: code-push release-cordova <appName> <platform> [options]

Options:
    --deploymentName, -d        指定部署的类型, 默认"Staging", 可以选择"Production"
    --description, --des        添加描述
    --disabled, -x              指定是否应立即下载此版本
    --mandatory, -m             指定此版本是否为强制更新版本
    --targetBinaryVersion, -t   指定需要更新的版本号, 如果省略, 则用 config.xml 里的版本号 (例如 1.1.0, ~1.2.3)
    
    例1: 发布android应用更新
    code-push release-cordova <appName> android --des "描述"
    
    例2: 部署android应用生产环境的热更新
    code-push release-cordova <appName> android -d "Production" --des "描述"
    
    例3: 部署ios应用的强制更新
    code-push release-cordova <appName> ios -m --des "描述"
    
    例4: 版本号>=1.2.3和<1.3.0的android应用更新
    code-push release-cordova <appName> android --des "描述" -t ~1.2.3

查看发布状态

# code-push deployment ls test

┌────────────┬─────────────────────────────┬──────────────────────┐
│ Name       │ Update Metadata             │ Install Metrics      │
├────────────┼─────────────────────────────┼──────────────────────┤
│ Production │ No updates released         │ No installs recorded │
├────────────┼─────────────────────────────┼──────────────────────┤
│ Staging    │ Label: v2                   │ Active: 40% (2 of 5) │
│            │ App Version: 1.0.0          │ Total: 2             │
│            │ Mandatory: No               │                      │
│            │ Release Time: 1 minute ago  │                      │
│            │ Released By:                │                      │
│            │ Description: 描述           │                      │
└────────────┴─────────────────────────────┴──────────────────────┘
// 给app在热更新服务器上创建应用
code-push app add <appName> <os> <platform>
例: code-push app add <appName> android cordova

// 删除应用
code-push app rm <appName>

// 查看热更新服务器上有哪些应用
code-push app list

// 查看部署状态
code-push deployment ls <appName>

// 查看部署状态及key值
code-push deployment ls <appName> -k

// 清空部署记录
code-push deployment clear <appName> <deploymentName>
例: 清空Staging状态的部署记录
code-push deployment clear <appName> Staging

// 删除自定义的部署状态
code-push deployment rm <appName> <deploymentName>

参考:
https://github.com/Microsoft/cordova-plugin-code-push
https://segmentfault.com/a/1190000008591456
https://www.jianshu.com/p/6bbb8020c29e






  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值