系统环境 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 key
和 Staging 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