react-native热更新之code-push

更多内容参见个人技术博客,无广告欢迎关注

 

文章部分内容参考:https://blog.csdn.net/qq_33323251/article/details/79437932

但是我在这个基础上进行了补充

code-push已经迁移到 APP Center 

-------------- 

React Native的出现为移动开发领域带来了两大革命性的创新:

  1. 整合了移动端APP的开发,不仅缩短了APP的开发时间,也提高了APP的开发效率。
  2. 为移动APP动态更新提供了基础。

本文将向大家分享React Natvie应用部署/动态更新方面的内容。

React Native支持大家用React Native技术开发APP,并打包生成一个APP。在动态更新方面React Native只是提供了动态更新的基础,对将应用部署到哪里,如何进行动态更新并没有支持的那么完善。好在微软开发了CodePush,填补React Native 应用在动态更新方面的空白。CodePush 是微软提供的一套用于热更新 React Native 和 Cordova 应用的服务。下面将向大家分享如何使用CodePush实时更新你的应用,后期会分享不采用CodePush,如何自己去实现React Native应用热更新。

CodePush简介(https://appcenter.ms/

CodePush 是微软提供的一套用于热更新 React Native 和 Cordova 应用的服务。
CodePush 是提供给 React Native 和 Cordova 开发者直接部署移动应用更新给用户设备的云服务。CodePush 作为一个中央仓库,开发者可以推送更新 (JS, HTML, CSS and images),应用可以从客户端 SDK 里面查询更新。CodePush 可以让应用有更多的可确定性,也可以让你直接接触用户群。在修复一些小问题和添加新特性的时候,不需要经过二进制打包,可以直接推送代码进行实时更新。

React Native应用程序由JavaScript文件和任何附带图像组成,这些图像由打包程序捆绑在一起并作为特定于平台的二进制文件(即.ipa或.apk文件)的一部分进行分发。应用程序发布后,更新JavaScript代码(例如修复错误,添加新功能)或图像资产,需要您重新编译和重新分发整个二进制文件,当然,这包括与商店相关的任何审核时间你要发布到。
 
CodePush插件通过使您的JavaScript和图像与您发布到CodePush服务器的更新保持同步,有助于立即在最终用户面前进行产品改进。通过这种方式,您的应用可以获得离线移动体验的好处,以及一旦可用,就可以获得侧面加载更新的“类似网络”的敏捷性。这是双赢的!
 
为了确保您的最终用户始终拥有应用程序的正常版本,CodePush插件会维护以前更新的副本,以便在您意外推送包含崩溃的更新时,它可以自动回滚。这样,您可以放心,在您有机会回滚服务器之前,您的新发布的敏捷性不会导致用户被阻止。这是一个双赢的局面!
 
注意:任何与本机代码相关的产品更改(例如,修改AppDelegate.m / MainActivity.java文件,添加新插件)都无法通过CodePush分发,因此必须通过相应的商店进行更新。
 

 

CodePush 可以进行实时的推送代码更新:

  • 直接对用户部署代码更新
  • 管理 Alpha,Beta 和生产环境应用
  • 支持 React Native 和 Cordova
  • 支持JavaScript 文件与图片资源的更新

CodePush开源了react-native版本,react-native-code-push托管在GitHub上。

安装与注册CodePush

使用CodePush之前首先要安装CodePush客户端。本文以OSX 10.11.5作为平台进行演示。

安装 CodePush CLI (CodePush CLI具体用法指南)

管理 CodePush 账号需要通过 NodeJS-based CLI。
只需要在终端输入 npm install -g code-push-cli,就可以安装了。
安装完毕后,输入 code-push -v查看版本,如看到版本代表成功。
目前我的版本是 2.1.9

PS. 
npm为NodeJS的包管理器,如果你没安装NodeJS请先安装。(关于nodejs、npm详细讲解及安装

创建一个CodePush 账号

在终端输入code-push register,会打开如下注册页面让你选择授权账号,其实也就是跳转到官网https://appcenter.ms/sign-in

可以选择用微软账号登录或GitHub账号登录

通过之后,code-push会告诉你 “access key”,复制到终端完成注册。

然后终端输入code-push login进行登陆,登陆成功后,你的session文件将会写在 /Users/你的用户名/.code-push.config

登陆成功

登陆

 

PS.相关命令

  • code-push login 登陆
  • code-push loout 注销
  • code-push access-key ls 列出登陆的token
  • code-push access-key rm <accessKye> 删除某个 access-key

在CodePush服务器注册app

为了让CodePush服务器知道你的app,我们需要向它注册app: 在终端输入code-push app add <appName>即可完成注册。

 code-push-add-app

 

注册完成之后会返回一套deployment key,该key在后面步骤中会用到。

心得:如果你的应用分为Android和iOS版,那么在向CodePush注册应用的时候需要注册两个App获取两套deployment key,如:

  1.  
    code-push app add MyApp-Android
  2.  
    code-push app add MyApp-iOS

PS.相关命令

  • code-push app add 在账号里面添加一个新的app
  • code-push app remove 或者 rm 在账号里移除一个app
  • code-push app rename 重命名一个存在app
  • code-push app list 或则 ls 列出账号下面的所有app
  • code-push app transfer 把app的所有权转移到另外一个账号

 

 

集成CodePush SDK

Android

下面我们通过如下步骤在Android项目中集成CodePush。
第一步:在项目中安装 react-native-code-push插件,终端进入你的项目根目录然后运行
npm install --save react-native-code-push

第二步:在Android project中安装插件。
CodePush提供了两种方式:RNPM 和 Manual,本次演示所使用的是RNPM。
运行npm i -g rnpm,来安装RNPM。

在React Native v0.27及以后版本RNPM已经被集成到了 React Native CL中,就不需要再进行安装了。

第三步: 运行 rnpm link react-native-code-push。这条命令将会自动帮我们在anroid文件中添加好设置。iOS、Android都配好了 successful linked ,下面的步骤已经自动完成了你只检查下就行。

         
          react-native-code-push has been successfully linked

在终端运行此命令之后,终端会提示让你输入deployment key,这是你只需将你的deployment Staging key输入进去即可,如果不输入则直接单击enter跳过即可。

第四步: 在 android/app/build.gradle文件里面添如下代码:

  1.  
    apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
  2.  
     

然后在/android/settings.gradle中添加如下代码:

  1.  
    include ':react-native-code-push'
  2.  
    project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
  3.  
     

第五步: 运行 code-push deployment ls -k <appName>获取 部署秘钥。默认的部署名是 staging,所以 部署秘钥(deployment key ) 就是 staging

 

第六步: 添加配置。当APP启动时我们需要让app向CodePush咨询JS bundle的所在位置,这样CodePush就可以控制版本。更新 MainApplication.java文件:


public class MainApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

@Override
protected String getJSBundleFile() {
// gradlew assembleRelease会把所有用到的JavaScript代码都打包内置到APK中
if (BuildConfig.IS_CODE_PUSH) {
return CodePush.getJSBundleFile(); // code-push热更新
}
return UpdateContext.getBundleUrl(MainApplication.this); // pushy热更新
}


// 设置为 true 将不弹出 toast
private boolean SHUTDOWN_TOAST = false;
// 设置为 true 将不打印 log
private boolean SHUTDOWN_LOG = false;

@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNFetchBlobPackage(),
new WeChatPackage(),
new UpdatePackage(),
new RNDeviceInfo(),
new RNI18nPackage(),
new RNCWebViewPackage(),
new RNGestureHandlerPackage(),
new WeChatPackage(),
new ReactVideoPackage(),
new VectorIconsPackage(),
new SplashScreenReactPackage(),
new LinearGradientPackage(),
new ImagePickerPackage(),
new JPushPackage(SHUTDOWN_TOAST, SHUTDOWN_LOG),
new CodePush(BuildConfig.CODE_PUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
);
}

@Override
protected String getJSMainModuleName() {
return "index";
}
};

@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}

@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

热更新有两张方案 中文社区提供的Pushy微软提供CodePush

 

关于deployment-key的设置 (关于Grande

在上述代码中我们在创建CodePush实例的时候需要设置一个deployment-key,因为deployment-key分生产环境与测试环境两种,所以建议大家在build.gradle中进行设置。在build.gradle中的设置方法如下:

打开android/app/build.gradle文件,找到android { buildTypes {} }然后添加如下代码即可:

buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
signingConfig signingConfigs.config
buildConfigField "String", "CODE_PUSH_KEY", CODE_PUSH_KEY_PRODUCTION
buildConfigField "boolean", "IS_CODE_PUSH", "true"
}
debug {
buildConfigField "String", "CODE_PUSH_KEY", CODE_PUSH_KEY_STAGING
buildConfigField "boolean", "IS_CODE_PUSH", "true"
}
}

 

心得:另外,我们也可以将deployment-key存放在gradle.properties中:

  1.  
    CODE_PUSH_KEY_PRODUCTION="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  2.  
    CODE_PUSH_KEY_STAGING="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# gradle.properties专门用来配置全局键值对数据的
# 将它从git版本控制中排除不用担心keystore文件等敏感信息泄漏
# BuildConfig或代码中应用取值 ${KEY}
MYAPP_RELEASE_STORE_FILE=./keystores/release.keystore
MYAPP_RELEASE_KEY_ALIAS=release
MYAPP_RELEASE_STORE_PASSWORD=111111
MYAPP_RELEASE_KEY_PASSWORD=111111
VERSION_CODE=6
VERSION_NAME=1.0.0
CODE_PUSH_KEY_PRODUCTION="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
CODE_PUSH_KEY_STAGING="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 

 

在android/app/build.gradle设置好deployment-key之后呢,我们就可以这样使用了:

@Override
protected String getJSBundleFile() {
// gradlew assembleRelease会把所有用到的JavaScript代码都打包内置到APK中
if (BuildConfig.IS_CODE_PUSH) {
return CodePush.getJSBundleFile(); // code-push热更新
}
return UpdateContext.getBundleUrl(MainApplication.this); // pushy热更新

第七步:修改versionName。
在 android/app/build.gradle中有个 android.defaultConfig.versionName属性,我们需要把 应用版本改成 1.0.0(默认是1.0,但是codepush需要三位数)。

  1.  
    android{
  2.  
    defaultConfig{
  3.  
    versionName "1.0.0"
  4.  
    }
  5.  
    }

至此Code Push for Android的SDK已经集成完成。

iOS

CodePush官方提供RNPM、CocoaPods与手动三种在iOS项目中集成CodePush的方式,接下来我就以RNPM的方式来讲解一下如何在iOS项目中集成CodePush。

第一步:在项目中安装react-native-code-push插件,终端进入你的项目根目录然后运行

  1.  
    npm install --save react-native-code-push
  2.  
     

第二步: 运行 rnpm link react-native-code-push。这条命令将会自动帮我们在ios中添加好设置。

在终端运行此命令之后,终端会提示让你输入deployment key,这是你只需将你的deployment Staging key输入进去即可,如果不输入则直接单击enter跳过即可。

关于deployment-key的设置

在我们想CodePush注册App的时候,CodePush会给我们两个deployment-key分别是在生产环境与测试环境时使用的,我们可以通过如下步骤来设置deployment-key。

----------------------

先讲一下Xcode配置相关知识

配置分为Project和Target,一个Project可以包含多个Target,每个Target可以定义不同的配置,也就是Project的配置整个项目通用,Target的配置优先级较高,是集成自Project 

例如:公司有两套环境 测试test 和 线上prod,那么我可以定义两个 superbuy-test 、superbuy-prod (这是一种解决方案当然还有其他的做法,例如预编译宏 BETA 等...)

rnpm link之后code-push-key配置在了Info.plist文件中(Info.plist相当于Android中的清单文件AndroidMainFest.xml)

如何让code-push-key自动区分测试环境还是正式环境能? 

Xcode中 User Defined 登场

User Defined 是一个Xcode中十分强大而不常使用的功能,它能够配置一些你在Interface Builder 中不能配置的属性,进而很大程度上提高开发效率。在项目中使用User Defined Setting可以定义一些Xcode编译使用的宏配置,为了实现App的一些常量或属性值在不同运行环境下配置不同,可以在User Defined Setting中定义一些有关应用程序的配置。 

问题来了:是配置在Project中还是Target中?

答案是都可以,其实你copy一个target,会生成两个Info.plist文件,所以我选择配置在target中

 

---------------------- 

1.用Xcode 打开项目 ➜ Xcode的项目导航视图中的PROJECT下选择你的Target ➜

选择Build Settings页签 ➜ 单击 + 按钮然后选择添加User-Defined Setting

 

 

 

添加User-Defined-Setting

2.然后输入CODE_PUSH_KEY(名称可以自定义)

设置    Staging deployment key

提示:你可以通过code-push deployment ls -k <APP_NAME> -k命令来查看deployment key。

3.打开 Info.plist文件,在CodePushDeploymentKey列的Value中输入$(CODE_PUSH_KEY)

引用CODEPUSH_KEY 

 

4.AppDelegate设置jsLocationPath,由于是兼容pushy和code-push,所以两种热更新方式可以切换

怎么切换?添加预编译宏 

配置 IS_CODE_PUSH=1

but 编译失败

有两个文件一样的命名,因为Objective-C一直没有Java中Package的概念,所以对于命名空间开发者都很无奈,都是通过加前缀来区别保证不同名

这是由于Pushy和code-push都引用到了SSZipArchive(三方解压神器SSZipArchive

 

使用CodePush进行热更新

App Store/Play Store审核政策

虽然Google Play和内部分布式应用程序(例如Enterprise,Fabric,HockeyApp)对如何使用CodePush发布更新没有限制,但iOS App Store及其相应的指南在您将解决方案集成到您的内容之前,应该注意更准确的规则。应用。

3.3.2段,自2015年以来,Apple开发者计划许可协议完全允许对JavaScript和资产进行无线更新 - 在此处可下载的最新版本(20170605)中这一裁决更为广泛:

解释的代码可以下载到应用程序,但只要这样的代码:(a)不会通过提供与提交给App的应用程序的预期和广告目的不一致的特性或功能来改变应用程序的主要目的。存储,(b)不为其他代码或应用程序创建商店或店面,并且(c)不绕过操作系统的签名,沙箱或其他安全功能。

CodePush允许您完全遵从这些规则,只要您推送的更新不会使您的产品与其原始App Store批准的意图明显不同。

为了进一步遵守Apple的指导原则,我们建议App Store分发的应用程序updateDialog在调用时不启用该选项sync,因为在App Store Review Guidelines中,它写成:

应用不得强制用户对应用评分,审核应用,下载其他应用或其他类似操作,以便访问应用的功能,内容或使用。

这不一定是这种情况updateDialog,因为它不会强迫用户下载新版本,但至少如果你决定展示它,你应该知道这个裁决。

设置更新策略

在使用CodePush更新你的应用之前需要,先配置一下更新控制策略,即:

  • 什么时候检查更新?(在APP启动的时候?在设置页面添加一个检查更新按钮?)
  • 什么时候可以更新,如何将更新呈现给终端用户?

最简单的方式是在根component中进行上述策略控制。

  1. 在 js中加载 CodePush模块:import codePush from 'react-native-code-push' 
  2. 在 componentDidMount中调用 sync方法,后台请求更新 codePush.sync()
// 第一种:
codePush.sync();

// 第二种:
codePush.sync({
    updateDialog: false,
    installMode: codePush.InstallMode.IMMEDIATE
});

// 第三种:
CodePush.sync({
    deploymentKey: 'deployment-key-here',
    updateDialog: {
        optionalIgnoreButtonLabel: '稍后',
        optionalInstallButtonLabel: '后台更新',
        optionalUpdateMessage: '有新版本了,是否更新?',
        title: '更新提示'
    },
    installMode: CodePush.InstallMode.IMMEDIATE
});
  • 三种更新的策略: 配置到installMode: 之后即可生效

    • IMMEDIATE 立即更新APP

    • ON_NEXT_RESTART 到下一次启动应用时

    • ON_NEXT_RESUME 当应用从后台返回时 

如果可以进行更新,CodePush会在后台静默地将更新下载到本地,等待APP下一次启动的时候应用更新,以确保用户看到的是最新版本。

如果更新是强制性的,更新文件下载好之后会立即进行更新。
如果你期望更及时的获得更新,可以在每次APP从后台进入前台的时候去主动的检查更新:
在应用的根component的componentDidMount中添加如下代码:

  1.  
    AppState.addEventListener("change", (newState) => {
  2.  
    newState === "active" && codePush.sync();
  3.  
    });
  4.  
     

发布更新

CodePush支持两种发布更新的方式,一种是通过code-push release-react简化方式,另外一种是通过code-push release的复杂方式。

第一种方式:通过code-push release-react发布更新

这种方式将打包与发布两个命令合二为一,可以说大大简化了我们的操作流程,建议大家多使用这种方式来发布更新。

命令格式:

code-push release-react <appName> <platform> 

eg:

  1.  
    code-push release-react MyApp-iOS ios
  2.  
    code-push release-react MyApp-Android android

再来个更高级的:

code-push release-react MyApp-iOS ios  --t 1.0.0 --dev false --d Production --des "1.优化操作流程" --m true 

其中参数--t为二进制(.ipa与apk)安装包的的版本;--dev为是否启用开发者模式(默认为false);--d是要发布更新的环境分Production与Staging(默认为Staging);--des为更新说明;--m 是强制更新。

关于code-push release-react更多可选的参数,可以在终端输入code-push release-react进行查看。

release-react常用命令

# Release a mandatory update with a changelog 强制更新包
code-push release-react MyApp-iOS ios -m --description "Modified the header color"

# Release an update for an app that uses a non-standard entry file name, and also capture
# the sourcemap file generated by react-native bundle
code-push release-react MyApp-iOS ios --entryFile MyApp.js --sourcemapOutput ../maps/MyApp.map

# Release a dev Android build to just 1/4 of your end users 灰度测试覆盖25%的用户
code-push release-react MyApp-Android android --rollout 25% --dev true

# Release an update that targets users running any 1.1.* binary Android版本在1.1.*的用户会得到更新,其他的版本不会更新
code-push release-react MyApp-Android android --targetBinaryVersion "~1.1.0"

CodePush客户端支持差异更新,因此即使您在每次更新时发布JS包和资源,您的最终用户也只会实际下载所需的文件。

有关release-react命令如何工作的更多详细信息,以及它公开的各种参数,请参阅CLI文档。此外,如果您希望自己处理react-native bundle命令,因此需要更灵活的解决方案release-react,请参阅release命令以获取更多详细信息。

 

另外,我们可以通过code-push deployment ls <appName>来查看发布详情与此次更新的安装情况。

第二中方式:通过code-push release发布更新

code-push release发布更新呢我们首先需要将js与图片资源进行打包成 bundle。

生成bundle

发布更新之前,需要先把 js打包成 bundle,如:

第一步: 在 工程目录里面新增 bundles文件:mkdir bundles

第二步: 运行命令打包 react-native bundle --platform 平台 --entry-file 启动文件 --bundle-output 打包js输出文件 --assets-dest 资源输出目录 --dev 是否调试
eg:
react-native bundle --platform android --entry-file index.android.js --bundle-output ./bundles/index.android.bundle --dev false

           生成bundle
           生成bundle

需要注意的是:

  • 忽略了资源输出是因为 输出资源文件后,会把bundle文件覆盖了。
  • 输出的bundle文件名不叫其他,而是 index.android.bundle,是因为 在debug模式下,工程读取的bundle就是叫做 index.android.bundle。
  • 平台可以选择 android 或者 ios。

发布更新

打包bundle结束后,就可以通过CodePush发布更新了。在终端输入
code-push release <应用名称> <Bundles所在目录> <对应的应用版本> --deploymentName: 更新环境 --description: 更新描述 --mandatory: 是否强制更新 
eg:
code-push release GitHubPopular ./bundles/index.android.bundle 1.0.6 --deploymentName Production --description "1.支持文章缓存。" --mandatory true

 推送更新到CodePush

 推 .     送更新到CodePush 

 

注意:

  1. CodePush默认是更新 staging 环境的,如果是staging,则不需要填写 deploymentName。
  2. 如果有 mandatory 则Code Push会根据mandatory 是true或false来控制应用是否强制更新。默认情况下mandatory为false即不强制更新。
  3. 对应的应用版本(targetBinaryVersion)是指当前app的版本(对应build.gradle中设置的versionName "1.0.6"),也就是说此次更新的js/images对应的是app的那个版本。不要将其理解为这次js更新的版本。
    如客户端版本是 1.0.6,那么我们对1.0.6的客户端更新js/images,targetBinaryVersion填的就是1.0.6。
  4. 对于对某个应用版本进行多次更新的情况,CodePush会检查每次上传的 bundle,如果在该版本下如1.0.6已经存在与这次上传完全一样的bundle(对应一个版本有两个bundle的md5完全一样),那么CodePush会拒绝此次更新。
    如图:
    对应一个版本有两个bundle的md5完全一样
    对应一个版本有两个bundle的md5完全一样

所以如果我们要对某一个应用版本进行多次更新,只需要上传与上次不同的bundle/images即可。如:
eg:
对1.0.6的版本进行第一次更新:
code-push release GitHubPopular ./bundles/index.android.bundle 1.0.6 --deploymentName Production --description "1.支持文章缓存。" --mandatory true 
对1.0.6的版本进行第二次更新:
code-push release GitHubPopular ./bundles/index.android.bundle 1.0.6 --deploymentName Production --description "1.新添加收藏功能。" --mandatory true

  1. 在终端输入 code-push deployment history <appName> Staging 可以看到Staging版本更新的时间、描述等等属性。
    eg:
    code-push release Equipment ./bundles 1.0.1

下面我们启动事先安装好的应用,看有什么反应:

           提示更新
           提示更新


应用启动之后,从CodePush服务器查询更新,并下载到本地,下载好之后,提示用户进行更新。这就是CodePush用于热更新的整个过程。

 

更多部署APP相关命令

  • code-push deployment add <appName> 部署
  • code-push deployment rename <appName> 重命名
  • code-push deployment rm <appName> 删除部署
  • code-push deployment ls <appName> 列出应用的部署情况
  • code-push deployment ls <appName> -k 查看部署的key
  • code-push deployment history <appName> <deploymentNmae> 查看历史版本(Production 或者 Staging)

调试/故障排除

查看这些日志的最简单方法是运行code-push debug您当前使用的特定平台的命令(例如code-push debug ios)。这将输出一个日志流,该日志流将被过滤为指定平台的CodePush消息。这样可以轻松识别问题,无需使用特定于平台的工具,也可以轻松浏览大量日志。

屏幕截图2016-06-21在10 15 42 am

此外,如果您对它们更熟悉,您还可以使用任何特定于平台的工具来查看CodePush日志。简单启动Chrome DevTools控制台,Xcode控制台(iOS),OS X控制台(iOS)和/或ADB logcat(Android),并查找带有前缀的消息[CodePush]

请注意,默认情况下,在发布版本中iOS上禁用了React Native日志,因此如果要在发布版本中查看它们,则需要对AppDelegate.m文件进行以下更改:

  1. 添加一个#import <React/RCTLog.h>声明。对于RN <v0.40,请使用:#import "RCTLog.h"

  2. 将以下语句添加到application:didFinishLaunchingWithOptions方法的顶部:

    RCTSetLogThreshold(RCTLogLevelInfo);

JavaScript API Reference

  • allowRestart
  • checkForUpdate
  • disallowRestart
  • getUpdateMetadata
  • notifyAppReady
  • restartApp
  • sync

其实我们可以将这些API分为两类,一类是自动模式,一类是手动模式。

自动模式

sync为自动模式,调用此方法CodePush会帮你完成一系列的操作。其它方法都是在手动模式下使用的。
codePush.sync 
codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress)): Promise<Number>; 
通过调用该方法CodePush会帮我们自动完成检查更新,下载,安装等一系列操作。除非我们需要自定义UI表现,不然直接用这个方法就可以了。
sync方法,提供了如下属性以允许你定制sync方法的默认行为

  • deploymentKey (String): 部署key,指定你要查询更新的部署秘钥,默认情况下该值来自于Info.plist(Ios)和MianActivity.java(Android)文件,你可以通过设置该属性来动态查询不同部署key下的更新。
  • installMode (codePush.InstallMode): 安装模式,用在向CodePush推送更新时没有设置强制更新(mandatory为true)的情况下,默认codePush.InstallMode.ON_NEXT_RESTART即下一次启动的时候安装。
  • mandatoryInstallMode (codePush.InstallMode):强制更新,默认codePush.InstallMode.IMMEDIATE。
  • minimumBackgroundDuration (Number):该属性用于指定app处于后台多少秒才进行重启已完成更新。默认为0。该属性只在installModeInstallMode.ON_NEXT_RESUME情况下有效。
  • updateDialog (UpdateDialogOptions) :可选的,更新的对话框,默认是null,包含以下属性
    • appendReleaseDescription (Boolean) - 是否显示更新description,默认false
    • descriptionPrefix (String) - 更新说明的前缀。 默认是” Description: “
    • mandatoryContinueButtonLabel (String) - 强制更新的按钮文字. 默认 to “Continue”.
    • mandatoryUpdateMessage (String) - 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.
    • optionalIgnoreButtonLabel (String) - 非强制更新时,取消按钮文字. Defaults to “Ignore”.
    • optionalInstallButtonLabel (String) - 非强制更新时,确认文字. Defaults to “Install”.
    • optionalUpdateMessage (String) - 非强制更新时,更新通知. Defaults to “An update is available. Would you like to install it?”.
    • title (String) - 要显示的更新通知的标题. Defaults to “Update available”

手动模式

codePush.allowRestart

codePush.allowRestart(): void; 
允许重新启动应用以完成更新。
如果一个CodePush更新将要发生并且需要重启应用(e.g.设置了InstallMode.IMMEDIATE模式),但由于调用了disallowRestart方法而导致APP无法通过重启来完成更新,
可以调用此方法来解除disallowRestart限制。
但在如下四种情况下,CodePush将不会立即重启应用:

  1. 自上一次disallowRestart被调用,没有新的更新。
  2. 有更新,但installModeInstallMode.ON_NEXT_RESTART的情况下。
  3. 有更新,但installModeInstallMode.ON_NEXT_RESUME,并且程序一直处于前台,并没有从后台切换到前台的情况下。
  4. 自从上次disallowRestart被调用,没有再调用restartApp

codePush.checkForUpdate

codePush.checkForUpdate(deploymentKey: String = null): Promise<RemotePackage>; 
向CodePush服务器查询是否有更新。
该方法返回Promise,有如下两种值:

  • null 没有更新
    通常有如下情况导致RemotePackage为null:

    1. 当前APP版本下没有部署新的更新版本。也就是说没有想CodePush服务器推送基于当前版本的有关更新。
    2. CodePush上的更新和用户当前所安装的APP版本不匹配。也就是说CodePush服务器上有更新,但该更新对应的APP版本和用户安装的当前版本不对应。
    3. 当前APP已将安装了最新的更新。
    4. 部署在CodePush上可用于当前APP版本的更新被标记成了不可用。
    5. 部署在CodePush上可用于当前APP版本的更新是"active rollout"状态,并且当前的设备不在有资格更新的百分比的设备之内。
  • A RemotePackage instance
    有更新可供下载。

eg:

  1.  
    codePush.checkForUpdate()
  2.  
    .then((update) => {
  3.  
    if (!update) {
  4.  
    console.log("The app is up to date!");
  5.  
    } else {
  6.  
    console.log("An update is available! Should we download it?");
  7.  
    }
  8.  
    });
  9.  
     

codePush.disallowRestart

codePush.disallowRestart(): void; 
不允许立即重启用于以完成更新。
eg:

  1.  
    class OnboardingProcess extends Component {
  2.  
    ...
  3.  
     
  4.  
    componentWillMount() {
  5.  
    // Ensure that any CodePush updates which are
  6.  
    // synchronized in the background can't trigger
  7.  
    // a restart while this component is mounted.
  8.  
    codePush.disallowRestart();
  9.  
    }
  10.  
     
  11.  
    componentWillUnmount() {
  12.  
    // Reallow restarts, and optionally trigger
  13.  
    // a restart if one was currently pending.
  14.  
    codePush.allowRestart();
  15.  
    }
  16.  
     
  17.  
    ...
  18.  
    }
  19.  
     

codePush.getUpdateMetadata 
codePush.getUpdateMetadata(updateState: UpdateState = UpdateState.RUNNING): Promise<LocalPackage>; 
获取当前已安装更新的元数据(描述、安装时间、大小等)。
eg:

  1.  
    // Check if there is currently a CodePush update running, and if
  2.  
    // so, register it with the HockeyApp SDK (https://github.com/slowpath/react-native-hockeyapp)
  3.  
    // so that crash reports will correctly display the JS bundle version the user was running.
  4.  
    codePush.getUpdateMetadata().then((update) => {
  5.  
    if (update) {
  6.  
    hockeyApp.addMetadata({ CodePushRelease: update.label });
  7.  
    }
  8.  
    });
  9.  
     
  10.  
    // Check to see if there is still an update pending.
  11.  
    codePush.getUpdateMetadata(UpdateState.PENDING).then((update) => {
  12.  
    if (update) {
  13.  
    // There's a pending update, do we want to force a restart?
  14.  
    }
  15.  
    });
  16.  
     

codePush.notifyAppReady 
codePush.notifyAppReady(): Promise<void>; 
通知CodePush,一个更新安装好了。当你检查并安装更新,(比如没有使用sync方法去handle的时候),这个方法必须被调用。否则CodePush会认为update失败,并rollback当前版本,在app重启时。
当使用sync方法时,不需要调用本方法,因为sync会自动调用。

codePush.restartApp 
codePush.restartApp(onlyIfUpdateIsPending: Boolean = false): void; 
立即重启app。
当以下情况时,这个方式是很有用的:

  1. app 当 调用 sync 或 LocalPackage.install 方法时,指定的 install mode是 ON_NEXT_RESTART 或 ON_NEXT_RESUME时 。 这两种情况都是当app重启或resume时,更新内容才能被看到。
  2. 在特定情况下,如用户从其它页面返回到APP的首页时,这个时候调用此方法完成过更新对用户来说不是特别的明显。因为强制重启,能马上显示更新内容。

代码签名

代码签名是一种为​​捆绑创建数字签名的方法,以后可以在安装之前在客户端进行验证。

开发人员想知道他们发布的代码是他们编写的代码。代码签名是提供此类保证的主要机制,可以帮助减轻或消除一大类中间人攻击。

首先,开发人员生成一个非对称密钥对:私钥将用于签署捆绑包; 捆绑签名验证的公钥。该CodePush CLI然后使用私钥签名过程中捆绑releaserelease-reactrelease-cordova命令。公钥随移动应用程序一起提供。控制密钥的生成和管理掌握在开发人员手中。

在release命令结束时,cli计算bundle的内容哈希并将此值放入使用私钥签名的JWT中。当codepush插件将包下载到设备时,它会检查.codepushrelease包含JWT 文件,并使用公钥验证JWT签名。如果验证失败,则不会安装更新。

密钥生成

代码签名支持用于签名的PEM编码的RSA密钥(非证书)。您可以通过openssl生成它们,如下所示:

生成私有RSA密钥并将其写入private.pem文件 
openssl genrsa -out private.pem  
将private.pem中的公钥导出到public.pem  
openssl rsa -pubout -in private.pem -out public.pem

 

操作步骤:

  1. 生成新的二进制更新,包括
    • 更新的codepush插件支持代码签名
    • 配置你的代码推送sdk使用你的公钥(请参阅相关的React Native SDK(iOS, Android)或Cordova SDK部分了解详情)
  2. 生成一个新的CodePush更新,该更新以新的二进制版本为目标并指定--privateKeyPath(或简称-k)参数值

eg:

code-push release-react superbuy-android android  --t 1.0.0 --des "test android code-push,带有公钥签名的更新包要加上参数-privateKeyPath指定私钥路径" -k ./private.pem 

-----------------------------

关于代码签名常见错误 

这是因为发布更新时没有指定-k参数,私钥位置

code-push release-react superbuy-android android  --t 1.0.0 --des "test android code-push,带有公钥签名的更新包要加上参数-privateKeyPath指定私钥路径" -k ./private.pem 

 

总结

上文已经介绍了CodePush在动态更新方面的一些特性,但CodePush也存在着一些缺点:

  1. 服务器在国外,在国内访问,网速不是很理想。
  2. 其升级服务器端程序并不开源的,后期微软会不会对其收费还是个未知数。
    如果在没有更好的动态更新React Native应用的方案的情况下,并且这些问题还在你的接受范围之内的话,那么CodePush可以作为动态更新React Native应用的一种选择。
    后期会向大家分享不采用CodePush,自己搭建服务器并实现React Native应用的动态更新相关的方案。

------------------ 

code-push常用命令 

  • 安装: npm install -g code-push-cli

  • 注册账号: code-push register

  • 登陆: code-push login

  • 注销: code-push logout

  • 添加项目: code-push app add [app名称]

  • 删除项目: code-push app remove [app名称]

  • 列出账号下的所有项目: code-push app list

  • 显示登陆的token: code-push access-key ls

  • 删除某个access-key: code-push access-key rm <accessKey>

  • 添加协作人员:code-push collaborator add <appName> next@126.com

  • 部署一个环境: code-push deployment add <appName> <deploymentName>

  • 删除部署: code-push deployment rm <appName>

  • 列出应用的部署: code-push deployment ls <appName>

  • 查询部署环境的key: code-push deployment ls <appName> -k

  • 查看部署的历史版本信息: code-push deployment history <appName> <deploymentName>

  • 重命名一个部署: code-push deployment rename <appName> <currentDeploymentName> <newDeploymentName>

---------------

 实际发布更新时常用操作步骤

  • 登录: code-push login

  • 列出账号下的所有项目: code-push app list

  • 列出应用的部署: code-push deployment ls MyApp

  • 查看部署的历史版本信息: code-push deployment history MyApp Staging 

  • 发布版本更新: code-push release-react MyApp ios -d Staging --des 'UI调整' --t '1.0.0'

  • 把更新推到另一个环境: code-push promote MyApp Staging Production

--------------- 

参考: 
https://docs.microsoft.com/en-us/appcenter/distribution/codepush/
https://github.com/Microsoft/react-native-code-push

 

posted @ 2019-04-11 19:15  一只特立独行的程序猿  阅读(8640)  评论(0编辑  收藏  举报