CodePush 是微软提供的一套用于热更新 React Native 和 Cordova 应用的服务。CodePush 是提供给 React Native 和 Cordova 开发者直接部署移动应用更新给用户设备的云服务。CodePush 作为一个中央仓库,开发者可以推送更新 (JS, HTML, CSS and images),应用可以从客户端 SDK 里面查询更新。
终端输入命令安装
npm install -g code-push-cli
在终端输入命令,会打开如下注册页面让你选择授权账号
code-push register
在这个网页可以选择Github。或者微软作为授权提供者,不过我觉得90%的开发者都会选择Github。
授权通过之后,CodePush会告诉你“access key”,复制此key到终端即可完成注册。然后终端输入code-push login
进行登陆,登陆成功后,你的session文件将会写在 /Users/你的用户名/.code-push.config。只要不主动退出(通过code-push logout
命令),登陆状态会一直有效。
在终端输入命令即可完成创建,注册完成之后会返回一套deployment key,包括Staging和Production。该key在后面步骤中会用到。
code-push app add
如果你的应用分为Android和iOS版,那么在向CodePush注册应用的时候需要注册两个App获取两套deployment key
code-push app add tolvgxAD android react-native
code-push app add tolvgxIOS ios react-native
ps.相关命令
查看所有app:code-push app ls
新建app:code-push app add
重命名app:code-push app rename
移除app:code-push app rm
这里分Android集成和iOS集成,二者共同的部分如下:
1.首先,在RN项目中安装codePush依赖
npm install --save react-native-code-push
2.运行命令自动构建关联
rnpm link react-native-code-push
在终端运行此命令之后,终端会提示让你输入deployment key,这是你只需将你的deployment Staging key输入进去即可,如果不输入则直接单击enter跳过分别在Android和IOS项目中进行配置。
运行自动构建关联命令一般在Android项目中不会完全成功,如果不成功,参考下面的手动配置。
1.在android/app/build.gradle文件里面添如下代码:
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
2.在/android/settings.gradle中添加如下代码:
include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
3.关于deployment-key的设置
打开android/app/build.gradle文件,修改android-buildTypes节点成如下:
buildTypes {
debug{
//省略了其他配置
// CodePush updates should not be tested in Debug mode
}
releaseStaging {
buildConfigField "String", "CODEPUSH_KEY", '"此处填写Staging key"'
}
release {
//省略了其他配置
buildConfigField "String", "CODEPUSH_KEY", '"此处填写Production key"'
}
}
4.配置CodePush类
当APP或者RN类启动时我们需要让app向CodePush咨询JS bundle的所在位置,这样CodePush就可以控制版本
这里分两种情况(不清楚的可以参考我的博客《ReactNative集成到现有原生应用&和完整RN项目的异同》):
(1)纯RN项目
在MainApplication中
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),
new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
);
}
(2)现有项目嵌入RN
在单独的RN类ReactNativeActivity中,1.将CodePush作为扩展加入Packages 2.将.setBundleAssetName("index.android.bundle")替换为.setJSBundleFile(CodePush.getJSBundleFile()),如图:
List reactPackages = Arrays.asList(
new MainReactPackage(),//默认
new LinearGradientPackage(),//引入Rn渐变控件
new CodePush(BuildConfig.CODEPUSH_KEY, ReactNativeActivity.this, BuildConfig.DEBUG),//codePush热更新
new TransMissonPackage()//通信
);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setJSMainModulePath("index")// ! 注意这里的index指向入口的js文件
// .setBundleAssetName("index.android.bundle")
.setJSBundleFile(CodePush.getJSBundleFile())// ! 此处为codePush加载JsBundle方式,默认为.setBundleAssetName("index.android.bundle")
// .addPackage(new MainReactPackage())
.addPackages(reactPackages)// ! 此处为扩展Packages,默认为.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
5.关于VersionName
在 android/app/build.gradle中有个android.defaultConfig.versionName属性,我们需要把应用版本改成三位,比如1.0需要修改成1.0.0,codepush需要三位数
至此Code Push for Android的SDK已经集成完成。
关于deployment-key的配置
在我们想CodePush注册App的时候,CodePush会给我们两个deployment-key分别是在生产环境与测试环境时使用的,我们可以通过如下步骤来设置deployment-key。
1.用Xcode 打开项目 ➜ Xcode的项目导航视图中的PROJECT
下选择你的项目 ➜ 选择Info页签 ➜ 在Configurations
节点下单击 + 按钮 ➜ 选择Duplicate "Release
➜ 输入Staging(名称可以自定义);
2.然后选择Build Settings
页签 ➜ 单击 + 按钮然后选择添加User-Defined Setting
3.然后输入CODEPUSH_KEY(名称可以自定义)
4.打开 Info.plist文件,在CodePushDeploymentKey列的Value中输入$(CODEPUSH_KEY)
至此Code Push for IOS的SDK已经集成完成。
一般常见的应用内更新时机分为两种,一种是打开APP就检查更新,一种是放在设置界面让用户主动检查更新并安装。
1.在 js中加载 CodePush模块:
import codePush from 'react-native-code-push'
2.在 componentDidMount
中调用 sync
方法,后台请求更新
codePush.sync()
如果有更新包可供下载则会在重启后生效。不过这种下载和安装都是静默的,即用户不可见。如果需要用户可见则需要额外的配置,如下:
componentDidMount(){
codePush.sync(
{
updateDialog: {
appendReleaseDescription: true,
title:'更新提示',
descriptionPrefix:'',
mandatoryUpdateMessage:'',
mandatoryContinueButtonLabel:'立即更新',
optionalUpdateMessage:'',
optionalIgnoreButtonLabel: '稍后更新',
optionalInstallButtonLabel: '立即更新',
},
installMode: codePush.InstallMode.IMMEDIATE,//ON_NEXT_RESTART,ON_NEXT_RESUME
deploymentKey: CODE_PUSH_DEBUG_KEY,
},
(status)=>{
switch (status) {
case codePush.SyncStatus.DOWNLOADING_PACKAGE:
this.refs.toast.show('资源加载中...', 2000);
break;
case codePush.SyncStatus.INSTALLING_UPDATE:
console.log(" INSTALLING_UPDATE");
break;
}
}
);
}
CodePush支持两种发布更新的方式,一种是通过code-push release-react
简化方式,另外一种是通过code-push release
的复杂方式。
方式一:通过code-push release-react
发布更新
这种方式将打包与发布两个命令合二为一,可以说大大简化了我们的操作流程,建议大家多使用这种方式来发布更新。
命令格式:
code-push release-react
示例:
code-push release-react tolvgxAD android --t 4.2.0 --dev false --d Staging --des "1.测试快捷发布 \n 2.优化操作流程" --m false
其中参数--t为二进制(.ipa与apk)安装包的的版本;--dev为是否启用开发者模式(默认为false);--d是要发布更新的环境分Production与Staging(默认为Staging);--des为更新说明;--m 是强制更新。
关于code-push release-react
更多可选的参数,可以在终端输入code-push release-react
进行查看。
方式二:通过code-push release
发布更新
(1)手动执行打包Bundle命令打包
命令格式:
react-native bundle --platform 平台 --entry-file 启动文件 --bundle-output 打包js输出文件 --assets-dest 资源输出目录 --dev 是否调试
示例:
react-native bundle --platform android --entry-file index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res --dev false
(2)发布更新
命令格式:
code-push release <应用名称> <对应的应用版本> --deploymentName: 更新环境 --description: 更新描述 --mandatory: 是否强制更新
示例:
code-push release tolvgxAD ./android/app/src/main/assets/index.android.bundle 4.2.0 --deploymentName Staging --description "4.测试ReactNative功能" --mandatory true
注意:对于对某个应用版本进行多次更新的情况,CodePush会检查每次上传的 bundle,如果在该版本下如1.0.6已经存在与这次上传完全一样的bundle(对应一个版本有两个bundle的md5完全一样),那么CodePush会拒绝此次更新。所以如果我们要对某一个应用版本进行多次更新,只需要上传与上次不同的bundle/images即可。
ps.相关命令
1、查看app的全部部署:code-push deployment ls
code-push deployment ls tolvgxAD
2、查看app的某个部署的发布更新的历史记录:code-push deployment history (一旦发布过更新就禁用:清除历史记录客户端加载的也是最后一次服务器bundle)
code-push deployment history tolvgxAD Staging
3、清除与部署相关的发布历史记录:code-push deployment clear
4、删除app的某个部署:code-push deployment remove (禁用:整个Production删除了,重新add后key就变了)
5、添加app的某个部署:code-push deployment add
6、查看部署的key:code-push deployment ls -k
1、code-push rollback tolvgxAD Staging
当执行这个命令时它会在tolvgxAD上的Staging部署上再次发布一个release,这个release的代码和元属性与Staging上倒数第二个版本一致。
2、code-push rollback tolvgxAD Staging --targetRelease v4
当执行这个命令时它会在tolvgxAD上的Staging部署上再次发布一个release,这个release的代码和元属性与Staging上v4版本一致。
注意:这个回滚是主动回滚,与自动回滚不一样
一个App通常不会是一个人开发,因此codePush添加合作者是很常见的。通过 code-push collaborator
来操作合作者相关命令。
Usage: code-push collaborator
命令:
add 对指定的项目添加一个新的合作者
remove 删除指定的项目中的合作者
rm 删除指定的项目中的合作者
list 列出指定项目中的所有合作者
ls 列出指定项目中的所有合作者
示例:
code-push collaborator add AppDemo [email protected] 添加一个合作者[email protected]到AppDemo这个App中
如果你用模拟器进行调试CodePush,在默认情况下是无法达到调试效果的,因为在开发环境下装在模拟器上的React Native应用每次启动时都会从NodeJS服务器上获取最新的bundle,所以还没等CodePush从服务器将更新包下载下来时,APP就已经从NodeJS服务器完成了更新。
Android
为规避这个问题在Android可以将开发环境的调试地址改为一个不可用的地址,如下图:
这样APP就无法连接到NodeJS服务器了,自然也就不能从NodeJS服务器下载bundle进行更新了,它也只能乖乖的等待从CodePush服务器下载更新包进行更新了。
IOS
在iOS中我们需要上文中讲到的生成bundle,将bundle包与相应的图片资源拖到iOS项目中如图:
然后呢,我们需要在AppDelegate.m中进行如下修改:
//#ifdef DEBUG
// jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
//#else
jsCodeLocation = [CodePush bundleURL];
//#endif
让React Native通过CodePush去获取bundle包即可。
至此,我们已经介绍了CodePush在动态更新方面的一些特性,但CodePush也存在着一些缺点:服务器在国外,在国内访问,网速不是很理想。如果在没有更好的动态更新React Native应用的方案的情况下,并且这些问题还在你的接受范围之内的话,那么CodePush可以作为动态更新React Native应用的一种选择。