Cocos Creator Android 热更新方案实践:逻辑更新、远程 Bundle 与线上回滚

这套方案服务于 Cocos Creator 3.8 Android 项目,目标不是"所有文件都用同一种方式更新",而是把不同类型的变更交给合适的机制:

  • 逻辑、首包 Bundle 内资源 :由 Cocos AssetsManager 更新,下载完成后重启游戏生效。
  • 玩法/大资源模块:使用 Cocos Remote Bundle,进入对应模块时按需下载,不需要重启。
  • 原生与引擎运行时内容:随 APK 发布,不能依赖热更新替换。

这样既能快速修复游戏逻辑,也不会让玩家在启动页下载全部玩法资源。

1. 为什么需要两套更新机制

AssetsManager 的工作方式是:下载 Manifest,逐文件校验并保存到应用可写目录,再通过搜索路径让新文件覆盖 APK 中的同路径文件。因此它适合需要"整体替换并重启"的内容。

Remote Bundle 是 Cocos 的资源加载机制。Cocos 构建时会为每个远程 Bundle 生成资源索引;玩家调用 loadBundle() 时,Cocos 再依据索引下载该模块真正使用的资源。因此它适合体积大、按模块访问的资源。

text 复制代码
启动时
  ├─ AssetsManager 检查 hot-update/version.manifest
  │    └─ 有逻辑更新:确认 → 下载 assets/** → 重启
  └─ 请求 remote-bundle-version.json
       └─ 只得到每个远程 Bundle 当前的索引版本

进入玩法模块
  └─ ResMgr.loadBundle(bundleName)
       └─ Cocos 下载 cc.config.<hash>.json 与所需资源

两者并不冲突,也不互相替代。

2. 目录与文件职责

静态服务器根目录示例:

text 复制代码
D:\HotUpdateServer\
├─ hot-update\
│  ├─ version.manifest            # 当前逻辑热更入口
│  ├─ project.manifest            # 当前逻辑热更完整清单
│  └─ 0.0.2\                      # 该逻辑版本实际增量文件
├─ remote\
│  └─ game_main\                  # Cocos 构建产生的远程 Bundle 文件
├─ remote-bundle-version.json      # 各 Remote Bundle 当前使用的 Cocos hash
└─ release-cache\android\0.0.1\data\
                                  # APK 0.0.1 的不可变基线

逻辑热更 Manifest

version.manifest 是轻量入口,通常包含版本号、远端 project.manifest 地址和资源根地址。

project.manifest 才是逐文件清单,记录本次需要更新的文件路径、MD5 和文件大小。AssetsManager 据此决定哪些文件下载、校验与写入可写目录。

本项目逻辑热更白名单为 assets/**。因此可以覆盖编译后的游戏脚本和首包 Bundle 内资源,但明确不发布以下运行时文件:

  • src/**
  • jsb-adapter/**
  • main.js
  • 原生 Java/Kotlin/C++、SDK、签名、权限与 AndroidManifest

后者必须随新 APK 发布。

Remote Bundle 的"版本表"为什么不是 Manifest

remote-bundle-version.json 只存"每个 Bundle 应当使用哪个 Cocos 构建索引":

json 复制代码
{
  "game_main": "c78e1",
  "game_mask": "1e953"
}

它不列出每个资源文件的 MD5。真正的资源清单已由 Cocos 自动生成:

text 复制代码
remote-bundle-version.json
  → remote/game_main/cc.config.c78e1.json
    → import/、native/、index.c78e1.js 及实际资源

项目的 RemoteBundleVersionMgr 请求版本表;ResMgr.loadBundle() 读取对应 hash 并传给 Cocos assetManager.loadBundle。后续资源依赖和下载完全由 Cocos 处理。

3. Bundle 划分建议

启动、登录、热更提示和基础兜底资源必须在首包,否则无法在网络不可用或远程资源故障时展示更新页面。

建议保留在首包:

  • game_loading,包括静态背景、uiLoginLoading、进度条、字体等;
  • 热更确认框和必要遮罩;
  • 启动、登录所需的最小依赖。

适合设为 Remote Bundle:

  • 高频变动的角色、特效、活动和玩法资源;
  • base_commoncommon_item 中不影响启动的内容;
  • 大型玩法模块及其场景资源;
  • 配置表 datas(前提是新配置能被旧逻辑兼容;否则要随逻辑热更或强制更新)。

不要为了"首包小"把所有内容都设为远程。登录后可后台预下载大厅和高频资源;玩家点击大玩法入口时仍应显示"资源准备中"与真实进度。

4. 构建前的固定配置

Android 构建中保持以下配置:

text 复制代码
Resource Server Address: http://<host>:8080/
MD5 Cache: 开启
处理构建模板文件的 MD5: 开启
Main Bundle Is Remote: 不开启

需要远程加载的 Bundle,还要在 Cocos 项目设置中勾选 Is Remote Bundle 。构建产物中出现 build/android/remote/ 才说明远程 Bundle 已真正产出;没有该目录时,发布脚本会拒绝执行。

MD5 缓存不能在同一条发布线中随意开关。开启后 Cocos 会生成 cc.config.<hash>.jsonindex.<hash>.js 等带 hash 文件;关闭或切换策略会造成新旧 APK、远程索引和缓存命名不兼容,正确处理是发新 APK 并建立新基线。

5. 发布流程

本项目统一使用 tools/publish-hot-update.js。它会输出 Remote Bundle、基线保存、Manifest 切换和 HTTP 校验的阶段日志;发布过程中长时间没有输出通常意味着文件复制、网络共享或静态服务异常,应根据最后一个日志阶段排查。

5.1 发布新的 APK 基线

新 APK 首次支持热更新,或基础运行时策略发生不兼容变更时,建立一个新的基线:

powershell 复制代码
node tools/publish-hot-update.js `
  --first-package `
  --build build/android `
  --server \\192.168.1.1\d$\HotUpdateServer `
  --base 0.0.1 `
  --hot-update-url http://192.168.1.1:8080/hot-update/

这个命令会:

  1. 合并发布 build/android/remote/
  2. 生成并上传 remote-bundle-version.json
  3. build/android/data 注入首包 Manifest 与热更搜索路径恢复代码;
  4. 将处理后的 data 保存为 release-cache/android/0.0.1/data
  5. 切换服务器根 hot-update/*.manifest
  6. 通过 HTTP 校验入口文件。

命令完成后必须再进入 build/android/proj 使用 Gradle 打包 APK。Cocos 每次重新构建都会覆盖 data,所以每次要发布 APK 都需要重新执行首包准备步骤。

5.2 发布逻辑或首包 Bundle 热更

逻辑版本始终相对同一个 APK 基线生成,而不是相对上一个热更包生成。这样用户从 0.0.1 跨过多个版本直接更新到 0.0.4,也不会漏掉 0.0.2、0.0.3 的文件。

powershell 复制代码
node tools/publish-hot-update.js `
  --build build/android `
  --server \\192.168.1.1\d$\HotUpdateServer `
  --base 0.0.1 `
  --version 0.0.2 `
  --hot-update-url http://192.168.1.1:8080/hot-update/

脚本会发布远程资源版本表,同时从基线与当前 data 比对,只导出变化的 assets/**hot-update/0.0.2/,最后再原子地切换根 Manifest(生产环境建议由 CDN 或对象存储的原子发布能力完成这一动作)。

逻辑热更检查到新版本后会弹窗;用户确认下载,下载完成后重启。强制更新可以在 version.manifest 中设置 forceUpdateVersion,让低于该版本的玩家只能更新,不能跳过。

5.3 只发布 Remote Bundle

仅改玩法资源且不改 assets/** 时:

powershell 复制代码
node tools/publish-hot-update.js `
  --remote-only `
  --build build/android `
  --server \\192.168.1.1\d$\HotUpdateServer `
  --base 0.0.1 `
  --server-url http://192.168.1.1:8080

该模式不修改 hot-update/version.manifest,不会触发逻辑热更弹窗和重启。脚本会先比较当前 assets/** 与基线:若发现逻辑或首包资源变动会直接失败,避免误把逻辑更新当作纯资源更新遗漏掉。

6. 线上回滚与紧急事故处理

发布前应将每一个"可切换入口文件"保留历史版本,至少包括:

  • hot-update/version.manifestproject.manifest
  • 每次 hot-update/<version>/ 的完整内容;
  • 每次 remote-bundle-version.json
  • Remote Bundle 的历史 hash 文件及其依赖资源。

6.1 逻辑热更事故

逻辑热更下载后必须重启,因此风险高于远程资源更新。出现启动崩溃、黑屏、核心流程不可用时:

  1. 立即停止发布,不要删除有问题版本目录;
  2. 将服务器根目录的 version.manifestproject.manifest 切回上一个已验证版本;
  3. 清理 CDN 缓存或以新文件名/短缓存时间确保入口 Manifest 尽快生效;
  4. 使用一台已安装旧 APK 的设备验证:重启后是否能拉到回滚版本并正常进入;
  5. 保存日志、故障 Manifest、构建产物和 hash,完成根因分析后再重新发布。

注意:已下载并重启到故障逻辑的用户,本地可写目录中已经存在坏文件。仅把服务器入口切回旧 Manifest,未必能自动删除其本地坏文件。因此线上要准备至少一种兜底:

  • 发布一个更高版本的修复热更包覆盖坏文件;
  • 在启动器中实现受控的"清理热更缓存并重启"能力;
  • 无法通过脚本逻辑恢复时,发布新 APK,并通过商店更新或强制更新拦截旧版本。

不要在服务端直接删除故障版本文件来"回滚"。已拿到该版本 Manifest 的客户端仍可能继续请求它,删除只会把可诊断的逻辑问题变成下载失败。

6.2 Remote Bundle 事故

Remote Bundle 的回滚通常更简单:将 remote-bundle-version.json 恢复为上一份已验证版本表。新进入模块的玩家会重新使用旧 cc.config.<hash>.json

但必须保留旧 hash 对应的所有文件;若发布时删除了历史 cc.config.<hash>.jsonindex.<hash>.js 或其依赖资源,回滚表也无法工作。生产环境应采用"新增/覆盖,不删除"的发布方式,并按用户版本淘汰周期做延迟清理。

已经加载到内存中的 Bundle 不会立刻切换;玩家重启游戏或下次重新加载该 Bundle 后才会读取新版本表。必要时可在活动入口增加版本刷新与资源准备提示,但不要在玩家对局中强制卸载资源。

6.3 CDN 与发布一致性

逻辑热更最危险的情况是:根 version.manifest 已指向新版本,但版本目录文件还没有完整上传,或 CDN 节点缓存不一致。发布顺序必须是:

text 复制代码
上传 hot-update/<version>/ 全部文件
        ↓
验证该目录的 project.manifest 与随机资源文件
        ↓
最后切换 hot-update/version.manifest、project.manifest
        ↓
验证公网/CDN 地址

入口 Manifest 建议短缓存或使用 CDN 刷新;带 hash 的 Remote Bundle 文件可长缓存。不要让入口文件和版本包采用同样的超长缓存策略。

7. 常见踩坑点

现象 常见原因 处理方式
构建后没有 remote/ Bundle 没有勾选 Is Remote Bundle 在项目设置中设置后重新构建
远程包 404 cc.config.<hash>.json 版本表写成了目录 MD5,或远程文件没有完整上传 版本表必须使用 Cocos 生成的 cc.config.<hash>.json 中的 hash;补传完整 Bundle
热更检查失败 version.manifest 地址不通、JSON 不合法、CDN 仍缓存旧入口 浏览器/Invoke-WebRequest 直接验证 URL;刷新 CDN
更新后黑屏或息屏恢复卡住 发布了引擎/启动运行时文件,或资源释放与恢复逻辑有问题 热更白名单仅限 assets/**;引擎与原生改动发新 APK;用 ADB 完整日志定位
热更下载后没有生效 未重启、首包没有注入搜索路径恢复代码,或缓存路径被错误覆盖 重新执行首包准备并打 APK;确认重启与搜索路径日志
逻辑热更没有弹框 实际只发布了 Remote Bundle,或 assets/** 未变化 Remote Bundle 按需下载不弹框;逻辑更新必须有新的 Manifest 版本
Remote Bundle 更新后看起来没有下载 玩家尚未进入该模块,或资源已在本地缓存 进入目标模块验证;测试时清应用数据或换干净设备
每次发布后文件越来越多 MD5 hash 文件保留了历史版本 这是兼容旧客户端所需;按版本覆盖率和保留周期清理,不要发布时直接删除
跨多版本更新漏文件 增量包相对上一个热更包而非 APK 基线生成 所有热更都相对 release-cache/android/<base>/data 生成
--remote-only 被脚本拒绝 当前 assets/** 也有变化 改用带 --version 的逻辑热更,或先确认不应发布的构建变动

8. 上线前检查清单

  • APK 基线、服务器基线目录和发布命令中的 --base 一致;
  • Cocos Resource Server Address 指向正式 CDN 根地址;
  • MD5 Cache 与模板 MD5 已开启,且没有在发布线中切换;
  • remote/remote-bundle-version.json 已通过公网地址验证;
  • hot-update/<version>/ 完整上传后,才切换根 Manifest;
  • 用干净设备验证:无更新启动、逻辑更新、普通更新跳过、强制更新、远程资源首次进入;
  • 用已安装旧版本并跨多个逻辑版本的设备验证;
  • 记录本次 APK 基线、热更版本、版本表、构建 commit、发布时间与回滚入口;
  • 已备份上一版根 Manifest 和 remote-bundle-version.json

9. 结论

逻辑热更应追求"小、可验证、可回滚";Remote Bundle 应追求"按需、可缓存、版本表可切换"。最重要的纪律是:所有逻辑热更相对 APK 基线生成;所有线上入口文件可快速回切;历史 hash 资源在确认淘汰前不删除。

相关推荐
00后程序员张1 小时前
iOS加固技术路线全面解析:Bitcode模式、源码模式与汇编模式对比及爱加密优势
android·汇编·ios·小程序·uni-app·cocoa·iphone
summerkissyou19871 小时前
android - 性能 - Perfetto - 内存分析教程及例子
android·性能
超开心~2 小时前
Android AAudio介绍及流程分析
android
取个名字太难了~2 小时前
最新手机外接相机实时传输 + 动态美颜完整项目源码
android·数码相机·智能手机·美颜·相机连接·demu
say_fall2 小时前
【Linux系统编程】文件操作基础:C标准库、系统调用、fd是什么和fd与FILE*的关系
android·linux·c语言
ii_best11 小时前
更新!移动端开发软件按键安卓版&手机助手v5.1.0上线!本地AI识别全面解锁,脚本开发再升级
android·人工智能·ios·按键精灵
方乐寺村12 小时前
s,使用libpng提升png图片的保存速度。接下来本文将阐述在Android中如何集成libpng,以及在使用过程中遇到的问题和最 ...
android
name好难取诶12 小时前
PHP 静态分析工具实战 PHPStan 和 Psalm 完全指南
android·开发语言·php
Lydia ,12 小时前
安卓基础-线性布局
android