解释Catalog与Hash文件的版本协商流程

解读

在国内 Unity 项目的热更新链路中,Catalog(通常叫 catalog.jsonmanifest.json)与 Hash 文件(如 files.md5bundleinfo.hash)是资源版本系统的“双保险”。
面试官问“版本协商流程”,本质是想确认候选人是否真正落地过 可灰度、可回滚、可断点续传 的整包/分包更新方案,而不仅是调用 Addressables 的 API。
答题时要体现 三段式交互:启动时客户端本地版本 ↔ CDN 远端版本 ↔ 最终运行时内存版本,并突出 差分、回退、强制更新 三大决策点。

知识点

  1. Catalog 角色:描述“哪个资源在哪个 Bundle 里”,含 BundleName、CRC、Dependencies、Hash128、Size、IsInRemote 等字段,驱动 Bundle 加载路径
  2. Hash 文件角色:只存 “文件级”或“Bundle 级”指纹(MD5/SHA1/Hash128),驱动差分下载,体积远小于 Catalog,便于第一时间拉取比对。
  3. 版本协商三阶段
    预比对阶段:客户端启动后先拉远端 Hash 文件(几十 KB),与 persistent 目录本地 Hash 做字典级 diff,得到 新增、变更、删除 三张表;
    Catalog 同步阶段:若 Hash diff 表非空,则按渠道(国内一般是阿里云 OSS + CDN 边缘节点)拉取 对应版本号的远端 Catalog,与 本地缓存 Catalog 做字段级 merge,解决依赖链冲突(如循环依赖或冗余);
    运行时锁定阶段:把 merge 后的 Catalog 写入 StreamingAssets 之外的只读缓存区,并生成 version.lock(文本记录本次协商出的版本号、时间戳、渠道号),后续 Addressables 加载只认此 lock 文件,保证 逻辑层与资源层版本原子一致
  4. 失败策略:任一步骤下载失败或校验失败,回退到上一版 lock 文件,并弹出 “弱网提示”;若检测到 大版本号不匹配(如 appStoreVersion=2.0 而远端强制要求≥2.1),则走 整包强更 跳应用商店。
  5. 灰度与渠道:国内发行常接 腾讯 Bugly、字节增长分析,在 Hash 文件里埋 grayPercent、whiteListUin 字段,协商阶段先判断用户是否命中灰度,再决定拉取哪一份 Catalog,实现资源按用户维度灰度

答案

版本协商流程可拆为 “轻量 diff → 精确 merge → 原子锁定” 三步:

  1. 启动后 异步拉取远端 Hash 文件(带 gzip 与 etag),与本地 persistent 目录的 lastHash.info 逐行比对,秒级得出差异列表
  2. 若差异列表为空,直接加载本地 Catalog,流程结束;
  3. 若存在差异,则按 “渠道+大版本+灰度标识” 拼接 URL,下载对应远端 Catalog,与本地 Catalog 做 字段级三路合并(本地、远端、备用回退),解决依赖冲突后写入缓存区
  4. 合并成功后,重新计算全量 Hash 并生成 version.lock,同时把 新 Catalog 与差异 Bundle 加入下载队列
  5. 下载队列采用 断点续传 + 分块校验(HTTP Range + CRC64),全部完成后刷新 Addressables 内部缓存热重启 ResourceLocator,实现 无感热更
  6. 任一步校验失败或网络中断,删除临时文件并回滚到上一版 lock保证玩家可进入游戏,仅提示“小版本更新失败,稍后再试”。

拓展思考

  1. Hash 文件粒度:若项目 Bundle 数 >5k,可把 Hash 拆成 “首包 Hash + 分模块 Hash”,协商阶段只拉首包,进入对应玩法时再懒加载模块 Hash降低首次流量(国内 4G 用户仍占 30%)。
  2. Catalog 二进制化:json 过大时,可改 MessagePack + LZ4减少 60% 体积,但需自己维护 向后兼容的序列化版本号防止老客户端解析失败
  3. 安全加固:Hash 文件与 Catalog 必须 RSA 签名公钥硬编码在 so 层防止运营商或恶意 Wi-Fi 篡改;国内渠道审计已把 “资源完整性校验” 列为 过包必检项
  4. WebGL 特例:浏览器无法使用 persistent 目录,需把 Hash 与 Catalog 写入 IndexedDB协商流程改为“先比对、再一次性批量下载”且需要处理浏览器缓存 304 与 CORS 跨域;面试时提及此点可体现 多端思维