React Native 项目整合 CodePush 完全指南

栏目: 服务器 · 发布时间: 7年前

内容简介:作者 | 钱凯杏仁移动开发工程师,前嵌入式工程师,关注大前端技术新潮流。

作者 | 钱凯

React Native 项目整合 CodePush 完全指南

杏仁移动开发工程师,前嵌入式工程师,关注大前端技术新潮流。

本文使用的环境:

  • React@16.3.1

  • React Native@0.55.4

  • react-native-code-push@5.3.4

  • Android SDK@23

  • Android Build Tool@23.0.3

  • Gradle@2.14.1

  • Android Gradle Plugin@2.2.3

Why CodePush?

CodePush 是微软提供的一个热更新前后台方案,它对 React Native 项目有很好的支持。

目前针对 React Native 的 hot update 方案有许多,但是 CodePush 是最成熟稳定的方案,它最大的特点是提供了完整的后台工具。它主要的优点是:

  • 微软出品,大厂保证

  • 良好的多环境支持(Testing,Staging, Production)

  • 灰度发布、自动回滚等等特性

  • 良好的数据统计支持:下载、安装、出错一目了然

  • 强大的 CLI 工具,一个终端搞定全部流程

由于 React Native 执行的是脚本 js 文件,对热更新有天然的亲和,有余力的团队可以尝试实现自己的框架,一个简单的实现思路是:

  • 修改加载 jsBundle   的代码,转而从指定的本地存储位置去加载。如果没有,下载 bundle, 并且本次打开使用 app 包中的 bundle。

  • 如果找到 jsBundle   文件,调用 api 比较版本号,如果不一致,则从指定服务器下载最新的 bundle 进行替换。

  • 通过反射调用私有方法,在下载完成的回调中更新运行时资源,从而能立即看到更新的效果。

  • 使用类似 google-diff-match-patch   的 diff 工具,生成差异化补丁,不必下载完整 bundle,从而大大减小补丁包体积。

网上有很多资料和源码,这里就不细述了。

后台配置

为了使用 Code Push 发布热更新,我们需要向微软服务注册我们的应用。这部分工作微软提供了强大的命令行工具: CodePush CLI

React Native 项目整合 CodePush 完全指南

安装 cli 工具

npm 全局安装:

npm install -g code-push-cli

关联账号

使用命令

code-push register

注册一个账号,可以直接使用 GitHub 账号授权,完成后将 token 复制回命令行中。

React Native 项目整合 CodePush 完全指南

使用 whoami   查看登录状态:

code-push whoami

注册应用

登录成功后,我们注册一个app:

code-push app add 你的App名称 android react-native

注意 一定要为 Android 和 iOS 分别注册 ,两者的更新包内容会有差异。

React Native 项目整合 CodePush 完全指南

查询状态

每个 App 有不同的运行时环境,比如 Production , Staging 等,我们也可以配置自己的环境。查看 App 的不同环境和部署状况:

code-push deployment ls 注册的app名称

React Native 项目整合 CodePush 完全指南

目前我们还没有发布任何更新,所以表中的状态是空的。

到这里就完成了后端的基本配置。

App端配置

版本兼容

安装 Code Push 环境前首先要 check 版本的兼容性问题,不同的RN版本需要使用不同的 Code Push,原则上我们建议将 RN 和 CodePush 都升级到最新版本。

下表是官方文档中的兼容性说明:

React Native version(s) Supporting CodePush version(s)
<0.14 Unsupported
v0.14 v1.3 (introduced Android support)
v0.15-v0.18 v1.4-v1.6 (introduced iOS asset support)
v0.19-v0.28 v1.7-v1.17 (introduced Android asset support)
v0.29-v0.30 v1.13-v1.17 (RN refactored native hosting code)
v0.31-v0.33 v1.14.6-v1.17 (RN refactored native hosting code)
v0.34-v0.35 v1.15-v1.17 (RN refactored native hosting code)
v0.36-v0.39 v1.16-v1.17 (RN refactored resume handler)
v0.40-v0.42 v1.17 (RN refactored iOS header files)
v0.43-v0.44 v2.0+ (RN refactored uimanager dependencies)
v0.45 v3.0+ (RN refactored instance manager code)
v0.46 v4.0+ (RN refactored js bundle loader code)
v0.46-v0.53 v5.1+ (RN removed unused registration of JS modules)
v0.54-v0.55 v5.3+ (Android Gradle Plugin 3.x integration)

安装包

使用命令:

npm info react-native-code-push

来查看包相关信息。

我们建议始终将RN、React以及一些相关库升级到最新版本。在根目录下使用命令:

npm install --save react-native-code-push

来安装最新版本的 CodePush。

也可以参照上面的兼容性表格,安装指定版本:

npm install --save react-native-code-push@5.1.4

工程配置(Android)

如果工程创建的时候比较早,可能是使用命令 create-react-native-app 来创建的,则需要在根目录执行:

npm run eject

来改变工程结构,防止后面的兼容性问题。

配置安卓工程,官方提供了两种途径:

  • 使用命令行工具 rnpm (现在已经被整合到React Native CLI工具中了)。执行

react-native link react-native-code-push
  • 手动配置

如果你是新手,或者对 gradle、安卓工程结构不了解,我们强烈建议执行一次手动配置,帮助理解到底发生了什么。

手动配置

step 1

android/settings.gradle 文件中添加:

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

这个文件定义了哪些 module 应该被加入到编译过程,对于单个 module 的项目可以不用需要这个文件,但是对于 multiModule 的项目我们就需要这个文件,否则 gradle 不知道要加载哪些项目。这个文件的代码在初始化阶段就会被执行。

我们添加的内容告诉 gradle:去 node_modules   目录下的   react-native-code-push   加载 CodePush 子项目。

step 2

android/app/build.gradle   中的   dependencies   方法中添加依赖:

...
dependencies {
    ...
    compile project(':react-native-code-push')
}

这样就能在主工程中引用到 CodePush 模块了。

step 3

继续在 android/app/build.gradle   中,添加在编译打包阶段 CodePush 需要执行的 task 引用:

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

这段代码其实就是调用了 project 对象的 apply 方法,传入了一个以 from 为 key 的 map。完整写出来就是这样的:

project.apply([from: '../../node_modules/react-native-code-push/android/codepush.gradle'])

apply from  和   apply plugin 的区别在于,前者是从指定 url 去加载脚本文件,后者则用是从仓库拉取 plugin id 对应的二进制执行包。

step 4

CodePush 发布有各种环境(deployment),默认有 Staging 和 Production,我们需要在 buildType   中配置对应的环境,并且设置   PushKey ,从而让 App 端的 CodePush RunTime 根据不同的健值来下载正确的更新包。

查询各个环境 Key 的方法是使用上文安装的 CLI 工具:

code-push deployment ls App名称 -k

React Native 项目整合 CodePush 完全指南

上表中的 Deployment Key   就是对应环境的 Key 值了。

android/app/build.gradle   中,配置 buildTypes:

buildTypes {

    // 对应Production环境
    release {
        ...
        buildConfigField "String", "CODEPUSH_KEY", '"从上述结果中复制的production值"'
        ...
    }

    // 对应Staging环境
    releaseStaging {
        // 从 release 拷贝配置,只修改了 pushKey
        initWith release
        buildConfigField "String", "CODEPUSH_KEY", '"从上述结果中复制的stagingkey值"'
    }

    debug {
        buildConfigField "String", "CODEPUSH_KEY", '""'
    }
}

注意这里不同 buildType 的命名,Staging 环境对应的 buildType 就叫 releaseStaging ,要符合这样的命名规范。

Debug 环境虽然用不到 CodePush, 但是也要配置空的 Key 值,否则会报错。

step 5

处理完引用关系后,我们修改 MainApplication.java ,在 App 执行时启动 CodePush 服务:

// 声明包
import com.microsoft.codepush.react.CodePush;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        ...
        // 重写 getJSBundleFile() 方法,让 CodePush 去获取正确的 jsBundle
        @Override
        protected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                // 创建一个CodePush运行时实例
                new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
                ...
            );
        }
    };
}

js端引入 Code Push

配置完项目工程后,我们将 CodePush 引入到 js 端。

首先将 App 的根组件包裹在 CodePush 中:

import codePush from "react-native-code-push";

AppRegistry.registerComponent('BDCRM', () => codePush(App));

CodePush 会在 App 启动后自动去 check 和更新最新的版本,我们可以添加一些配置,让它在进入后台的时候也执行检查:

let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL };
AppRegistry.registerComponent('BDCRM', () => codePush(codePushOptions)(App));

CodePush js端的 api 不多,我们可以用这些 api 控制更新的一系列流程,常用的有:

// 检测是否有更新包可用
codePush.checkForUpdate(deploymentKey: String = null, handleBinaryVersionMismatchCallback: (update: RemotePackage) => void): Promise<RemotePackage>;

// 获取本地最新更新包的属性
codePush.getCurrentPackage(): Promise<LocalPackage>;

// 重启app(即使不用在 Hot Updating,也挺有用的)
codePush.restartApp(onlyIfUpdateIsPending: Boolean = false): void;

// 手动进一次更新
codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress), handleBinaryVersionMismatchCallback: function(update: RemotePackage)): Promise<Number>;

更多详细信息见文档。

使用 CodePush CLI 发布更新

完成前后端的配置,打包发布应用后,后续的改动我们就能通过 CLI 工具来发布啦!

升级前首先要 check:

  • 应用的版本号要有更新(app/build.gradle: defaultConfig/versionName)

  • js bundle 要有改动,Code Push 会 diff 前后版本,如果代码一致会认为是无效的更新包

打开终端,进入到工程目录,完整发布命令是:

code-push release-react <appName> <platform>
[--bundleName <bundleName>]
[--deploymentName <deploymentName>]
[--description <description>]
[--development <development>]
[--disabled <disabled>]
[--entryFile <entryFile>]
[--gradleFile <gradleFile>]
[--mandatory]
[--noDuplicateReleaseError]
[--outputDir <outputDir>]
[--plistFile <plistFile>]
[--plistFilePrefix <plistFilePrefix>]
[--sourcemapOutput <sourcemapOutput>]
[--targetBinaryVersion <targetBinaryVersion>]
[--rollout <rolloutPercentage>]
[--privateKeyPath <pathToPrivateKey>]
[--config <config>]

命令参数很多,但用途都一目了然,嫌每次打麻烦的话,做成脚本也可以。

一般来说,我们发布应用首先会在测试环境进行稳定性测试,通过后再发布到生产环境中:

  • 打包发布 Staging 环境

code-push release-react 应用名 --platform android --deploymentName Staging --description "修复一些bug"

这样,我们 Staging 环境就可以收到更新推送啦,具体加载新 bundle 的实际,和我们在应用中配置的策略有关,上文已经介绍过了。

  • 测试 ok 后,提升(Promoting)到 Production 环境,并且进行灰度20%发布

code-push promote 应用名 Staging Production --rollout 20%
  • 在生产环境验证 ok,使用 patch 将灰度修改为100%,进行全网发布:

code-push patch 应用名 Production -rollout 100%

以上就是按照 测试 - 灰度 - 全部发布 步骤的一个典型 CodePush 发布工作流。

总体来说,CodePush 能满足我们灰度发布 React Native 应用的大部分需求了,由微软提供的服务器端支持可以节省很多工作,是一个成熟可靠的方案。如果要说缺点,可能有几个需要考虑一下:

  • 服务器速度,国内网络状况可能会影响下发的成功率和效率。

  • 污染代码,在 js 端必须将根节点包裹到 CodePush 模块中去,污染了代码。

  • 冗余,如果只是想要简单的下发小体积的 js bundle,CodePush 显得太“重”,过于冗余了,这时候用轻量化的方案更好。

总之,我们根据自己项目的需要去进行选型就好了!

更多细节,可以参考文档

全文完

以下文章您可能也会感兴趣:

我们正在招聘 Java 工程师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。

React Native 项目整合 CodePush 完全指南

杏仁技术站

长按左侧二维码关注我们,这里有一群热血青年期待着与您相会。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Clean Architecture

Clean Architecture

Robert C. Martin / Prentice Hall / 2017-9-20 / USD 34.99

Practical Software Architecture Solutions from the Legendary Robert C. Martin (“Uncle Bob”) By applying universal rules of software architecture, you can dramatically improve developer producti......一起来看看 《Clean Architecture》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具