React Native 项目整合 CodePush 全然指南
作者 | 钱凯
https://mmbiz.qpic.cn/mmbiz_jpg/ibm2sb53lRhyW3xoCtaqg02KEXCAMdfQBBVbQAkcc8U0KLCcAr998hrwbulN8ic7TzwrV2PpL31Dib8LySMe6hh1Q/640?wx_fmt=jpeg" alt="640?wx_fmt=jpeg" />
杏仁移动开发project师,前嵌入式project师。关注大前端技术新潮流。
本文使用的环境:
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 比較版本号号,假设不一致,则从指定server下载最新的 bundle 进行替换。通过反射调用私有方法。在下载完毕的回调中更新运行时资源。从而能马上看到更新的效果。
使用相似
google-diff-match-patch的 diff 工具。生成差异化补丁。不必下载完整 bundle,从而大大减小补丁包体积。
网上有非常多资料和源代码。这里就不细述了。
后台配置
为了使用 Code Push 公布热更新,我们须要向微软服务注冊我们的应用。
这部分工作微软提供了强大的命令行工具:CodePush CLI。
wx_fmt=png" alt="640?
wx_fmt=png" />
安装 cli 工具
npm 全局安装:
npm install -g code-push-cli
关联账号
使用命令
code-push register
注冊一个账号,能够直接使用 GitHub 账号授权。完毕后将 token 复制回命令行中。
wx_fmt=png" />
使用 whoami 查看登录状态:
code-push whoami
注冊应用
登录成功后,我们注冊一个app:
code-push app add 你的App名称 android react-native
注意一定要为 Android 和 iOS 分别注冊,两者的更新包内容会有差异。
https://mmbiz.qpic.cn/mmbiz_png/ibm2sb53lRhzxaMSGDemUgg3KCXWR3Epv5YYLU1402029M3sz8rMcZZ0qj2tKGlsa5ia3g4Tcyqe5Eu96pS9LJLQ/640?wx_fmt=png" alt="640?
wx_fmt=png" />
查询状态
每一个 App 有不同的运行时环境。比方 Production,Staging等,我们也能够配置自己的环境。查看 App 的不同环境和部署状况:
code-push deployment ls 注冊的app名称
wx_fmt=png" alt="640?wx_fmt=png" />
眼下我们还没有公布不论什么更新。所以表中的状态是空的。
到这里就完毕了后端的基本配置。
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
project配置(Android)
假设project创建的时候比較早。可能是使用命令create-react-native-app来创建的,则须要在根文件夹运行:
npm run eject
来改变project结构。防止后面的兼容性问题。
配置安卓project,官方提供了两种途径:
使用命令行工具
rnpm(如今已经被整合到React Native CLI工具中了)。运行
react-native link react-native-code-push
手动配置
假设你是新手,或者对 gradle、安卓project结构不了解,我们强烈建议运行一次手动配置,帮助理解究竟发生了什么。
手动配置
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')
}
这样就能在主project中引用到 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
https://mmbiz.qpic.cn/mmbiz_png/ibm2sb53lRhzxaMSGDemUgg3KCXWR3EpvibUAL8Y0nYl91dqVSa2qibicTXy736HJH0I5XeOoicZyksrJWXlQOa1w2g/640?wx_fmt=png" alt="640?wx_fmt=png" />
上表中的 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
配置完项目project后,我们将 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 前后版本号。假设代码一致会觉得是无效的更新包
打开终端,进入到project文件夹,完整公布命令是:
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 应用的大部分需求了,由微软提供的server端支持能够节省非常多工作。是一个成熟可靠的方案。假设要说缺点,可能有几个须要考虑一下:
server速度。国内网络状况可能会影响下发的成功率和效率。
污染代码,在 js 端必须将根节点包裹到 CodePush 模块中去,污染了代码。
冗余,假设仅仅是想要简单的下发小体积的 js bundle。CodePush 显得太“重”,过于冗余了。这时候用轻量化的方案更好。
总之,我们依据自己项目的须要去进行选型就好了!
很多其它细节,能够參考文档
全文完
下面文章您可能也会感兴趣:
我们正在招聘 Java project师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。
杏仁技术站
长按左側二维码关注我们。这里有一群热血青年期待着与您相会。
React Native 项目整合 CodePush 全然指南的更多相关文章
- Expo大作战(三)--针对已经开发过react native项目开发人员有针对性的介绍了expo,expo的局限性,开发时项目选型注意点等
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- 【腾讯Bugly干货分享】React Native项目实战总结
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 “8小时内拼工作,8小时外拼成长 ...
- React Native 项目运行在 Web 浏览器上面
React Native 的出现,让前端工程师拥有了使用 JavaScript 编写原生 APP 的能力.相比之前的 Web app 来说,对于性能和用户体验提升了非常多. 但是 React Nati ...
- React Native 项目实战-Tamic
layout: post title: React Native 项目实战 date: 2016-10-18 15:02:29 +0800 comments: true categories: Rea ...
- react native项目启动需要做的操作
一.启动: 1.查看端口(默认8081是否被占用) netstat -ano 可以查看所有的进程 2.netstat -ano | findstr "8081" 查看某个端口 ...
- React Native项目集成iOS原生模块
今天学习一下怎么在React Native项目中集成iOS原生模块,道理和在iOS原生项目中集成React Native模块类似.他们的界面跳转靠的都是iOS原生的UINavigationContro ...
- 安装android Studio和运行react native项目(基础篇)
ANDROID_HOME环境变量 确保ANDROID_HOME环境变量正确地指向了你安装的Android SDK的路径. 打开控制面板 -> 系统和安全 -> 系统 -> 高级系统设 ...
- 第一个React Native项目
1>下图操作创建第一个React Native项目: 用Xcode运行界面如下: 记住: 在使用项目文件期间,终端记住不要关闭的哟!!! 改变了程序代码,需要刷新运行,使用快捷键: Comman ...
- React Native 项目常用第三方组件汇总
React Native 项目常用第三方组件汇总 https://www.jianshu.com/p/d9cd9a868764?utm_campaign=maleskine&utm_conte ...
随机推荐
- [BZOJ5427]最长上升子序列/[BZOJ4282]慎二的随机数列
[BZOJ5427]最长上升子序列/[BZOJ4282]慎二的随机数列 题目大意: 给你一个长度为\(n(n\le10^5)\)的整数序列,其中有一些数已经模糊不清了,现在请你任意确定这些整数的值,使 ...
- BZOJ 5381 or & Codeforces 623E Transforming Sequence DP+NTT
两道题题意都是一样的 不过$CF$的模数是$10^9+7$ 很简单的分析发现$A_i$项一定要有一个之前没有出现过的二进制位才能满足条件 考虑$DP$来做 设$f_{i,j}$表示$i$个数用了二进制 ...
- Python 线程和进程和协程总结
Python 线程和进程和协程总结 线程和进程和协程 进程 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位: 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其 ...
- Unsupported major.minor version ,
一.错误现象: 当改变了jdk版本时,在编译java时,会遇到Unsupported major.minor version错误. 错误信息如下 : Unsupported major.minor ...
- * -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]’
错误描述: * -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object fr ...
- CSS网页布局垂直居中整理
一.使用CSS3处理垂直居中方式 1.使用Flex布局处理(推荐),简单好用 body,html{ width:100%; height:100%; } .out { width: 20%; heig ...
- libnids使用 (转)
http://blog.csdn.net/kl222/article/details/6248827---原始链接 Libnids是一个用于网络入侵检测开发的专业编程接口,它使用了Libpcap所以它 ...
- yolov3源码darknet在vscode下调试
1. 安装配置: https://pjreddie.com/darknet/yolo/ darknet文件夹下make命令搞定: 2. 配置vscode 打开安装好的vscode并安装扩展C/C++( ...
- ArrayList vs LinkedList 空间占用
空间占用上,ArrayList完胜 看下两者的内存占用图 这三个图,横轴是list长度,纵轴是内存占用值.两条蓝线是LinkedList,两条红线是ArrayList,可以看到,LinkedLis ...
- windows下vbs脚本隐藏控制台
每次想写python代码时,都需要打开IDE进行编写,并且需要创建许多小文件.如果使用jupyter就能够直接书写.但是jupyter需要手动通过控制台打开,这不够方便.通过把jupyter note ...