热更新让你绕过应用商店的审核,直接把新逻辑推送给用户。本文从架构原理、推送流程、关键代码到生产注意事项,完整梳理一遍。
一、为什么 React Native 能热更新
React Native 应用由两层组成:Native 壳 (Java / OC / Swift 编译的原生代码)和 JS Bundle(真正的业务逻辑)。Native 壳负责渲染和桥接系统 API,JS Bundle 负责组件逻辑、路由、状态管理------几乎全部业务代码都在这里。
换句话说:换 bundle 就等于换 App 逻辑,用户不需要重新安装 App。这就是热更新的本质。
⚠️ 关键限制 :热更新只能更新 JS bundle 和图片等资源文件,不能修改 Native 代码(如新增原生模块、修改 Manifest 权限)。涉及 Native 变更必须走完整发版流程。
二、完整推送流程
从开发者改代码到用户看到新版本,整个链路分为三个阶段:打包上传、启动检查、下载生效。
💡 bundle 文件本身走 CDN 下发,而不是应用服务器------bundle 通常几 MB 到十几 MB,CDN 更快更省钱。
三、后端接口设计
后端核心是一个版本检查接口,App 启动时携带当前信息请求,服务端决定是否推送及推哪个包。
请求参数
ini
GET /api/ota/check
?appVersion=1.2.0 // Native 壳版本(很重要!)
&platform=android // android / ios / harmony
&channel=production // 灰度分组
¤tBundle=20240801 // 当前 bundle 版本号
响应结构
json
{
"hasUpdate": true,
"bundleVersion": "20240810",
"bundleUrl": "https://cdn.example.com/bundles/android_20240810.zip",
"md5": "a1b2c3d4e5f6...", // 完整性校验
"mandatory": false, // 是否强制立即更新
"description": "修复搜索崩溃问题" // 更新说明(可选)
}
⚠️
appVersion字段至关重要------如果新 bundle 调用了新的 Native API,但用户 App 还是旧版 Native 壳,就会直接 crash。服务端必须按appVersion分流,只给兼容的 Native 版本推对应的 bundle。
四、客户端核心逻辑
以下是自建 OTA 的客户端核心流程,使用 CodePush 或 pushy 的项目原理相同,只是封装了这些步骤。
javascript
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV();
async function checkOTAUpdate() {
// 1. 读取当前本地版本号
const currentBundle = storage.getString('bundleVersion') ?? 'base';
const appVersion = DeviceInfo.getVersion();
// 2. 请求检查接口
const res = await fetch(
`https://ota.example.com/api/check?appVersion=${appVersion}¤tBundle=${currentBundle}`
);
const { hasUpdate, bundleUrl, bundleVersion, md5, mandatory } = await res.json();
if (!hasUpdate) return;
// 3. 后台下载新 bundle
const localPath = await downloadBundle(bundleUrl);
// 4. MD5 校验(防包损坏 / 防中间人篡改)
const valid = await verifyMD5(localPath, md5);
if (!valid) {
console.warn('OTA: MD5 mismatch, aborting');
return;
}
// 5. 替换 bundle,持久化路径和版本号
await replaceBundle(localPath);
storage.set('bundleVersion', bundleVersion);
// 6. 生效策略:强制立即重启 or 等下次冷启动
if (mandatory) {
RNRestart.Restart(); // 立即重载
}
// 非强制:下次启动自动用新 bundle
}
五、生产环境必须处理的问题
① 灰度发布
服务端在 /check 接口中,根据用户 ID 或设备 ID 做分桶,先推送 1%,监控崩溃率和 ANR,稳定后逐步扩量到 10% → 50% → 100%。这套逻辑完全在后端控制,客户端无需感知。
② 回滚机制
本地需保留最近 2~3 个历史 bundle(不能只存最新的)。当新 bundle 首次加载后崩溃率超过阈值,客户端自动回退到上一个版本,并上报异常告警后端关闭该批次推送。
③ 差量更新(bsdiff)
全量 bundle 通常几 MB 到十几 MB,用户流量成本高。成熟的 OTA 系统会使用 bsdiff 算法,只下发新旧 bundle 的差异 patch,客户端本地合并,包大小可降至几十 KB。
④ Native 版本兼容矩阵
维护一张 native_version → compatible_bundle_versions 的映射表,确保下发给 Native 1.0 壳的 bundle 不会调用只有 Native 1.1 才有的 API。
六、主流方案对比
| 方案 | 提供方 | 适合场景 | 主要限制 |
|---|---|---|---|
| CodePush | Microsoft (App Center) | 快速接入,国际项目 | 国内访问速度差,免费额度有限 |
| pushy | React Native 中文网 | 国内项目,合规友好 | 依赖第三方服务,定制空间有限 |
| 自建 OTA 服务 | 自研 | 大厂、高定制、灰度精细化 | 开发维护成本高 |
⚠️ HarmonyOS / RNOH 注意 :RNOH 目前对 CodePush 的支持不完整。HarmonyOS 平台做热更新需要关注华为应用市场的合规要求,以及 RNOH bundle 格式是否与 Android/iOS 完全一致------建议在自建 OTA 服务中按
platform字段分别管理 bundle。
七、小结
- 热更新的本质是替换 JS bundle,Native 层不动
- 后端核心是版本检查接口 + CDN 分发,bundle 文件本身走 CDN 而非应用服务器
- 生产环境必须有灰度、回滚、MD5 校验三件套
appVersion(Native 版本)要传给服务端,避免 bundle 与 Native API 不兼容导致 crash- 国内项目推荐 pushy 或自建;HarmonyOS 需单独处理平台兼容