跳转至

HybridCLR

核心机制

HybridCLR 不是“在 IL2CPP 上开一个 JIT”,而是把 IL2CPP 从纯 AOT 运行时扩展为 AOT + Interpreter 的混合运行时:1

主工程 AOT 代码
  → IL2CPP 编译为 C++ / 机器码

热更程序集
  → 运行时加载 DLL
  → HybridCLR 解释执行 IL
  → 通过补充元数据访问 AOT 泛型与类型

这样既保留了 iOS、主机等平台对 JIT 的限制,又能在运行时加载新的 C# 程序集。

基础配置

  • Scripting Backend 切换为 IL2CPP;
  • Api Compatibility Level 使用 .NET 4.x 或 .NET Framework;
  • 按项目版本安装 HybridCLR 包与生成器;
  • 热更程序集与 AOT 主工程使用兼容的 Unity / .NET API 基线;
  • 在目标平台上验证 GC、线程和热更程序集加载顺序。

配置只是起点

真正决定上线稳定性的是程序集划分、AOT 泛型补充、裁剪保护、构建产物校验与回滚策略,而不是几个 Inspector 选项。

补充元数据

AOT 泛型与某些类型实例在编译期未出现时,IL2CPP 不会生成对应实现。HybridCLR 通过补充元数据(Supplementary Metadata)让解释器能够构造这些类型。

构建时通常需要:

  1. 收集热更程序集引用到的 AOT 泛型与类型;
  2. 生成补充元数据 DLL;
  3. 随包或随热更内容分发;
  4. 加载热更程序集前调用运行时 API 加载元数据;
  5. 对缺失元数据做启动期校验,而不是等玩家进入玩法后崩溃。
// 结构示意:实际 API 名称与调用顺序以项目使用的 HybridCLR 版本为准
RuntimeApi.LoadMetadataForAOTAssembly(
    metadataDllBytes,
    HomologousImageMode.SuperSet);

程序集切分

适合放进热更程序集的内容:

  • 活动玩法与业务逻辑;
  • UI 流程与配置解析;
  • 低风险的战斗规则;
  • 需要快速修复的模块。

更适合留在 AOT 主工程的内容:

  • 引擎底层封装;
  • 高频数学与物理热点;
  • 启动引导与热更加载器;
  • 平台 SDK 桥接;
  • 安全与签名校验。

切分原则不是“能热更的都热更”,而是把高频性能路径与低稳定性依赖留在 AOT 侧。

裁剪与链接保护

IL2CPP 的 Managed Stripping 会删除未被静态引用的代码。热更程序集在构建期不可见,反射与序列化使用的类型尤其容易被裁剪。

需要:

  • 使用 link.xml 保护类型与成员;
  • 用生成的 AOT 引用代码显式引用泛型实例;
  • 在构建后检查裁剪日志;
  • 在真机 Development Build 上跑完整热更加载流程;
  • 对缺失类型记录清晰错误,而不是静默回退。

加载与回滚

启动
  → 校验热更清单
  → 加载补充元数据
  → 加载热更程序集
  → 初始化入口
  → 成功进入游戏 / 失败回退内置程序集

更完整的下载、裁剪、回滚和真机测试问题,整理在热更新常见问题与排查。

热更清单至少包含:

  • 程序集 ID、版本、哈希;
  • 对应客户端版本范围;
  • 补充元数据版本;
  • 依赖项与加载顺序;
  • 失败回退策略。

性能与兼容性边界

关注点 说明
解释执行开销 热更代码通常慢于 AOT,不应承担极端热点
跨程序集调用 频繁反射与动态调用会增加开销
泛型组合 缺失元数据会在运行时暴露
内存 程序集、元数据与解释器状态会占用额外内存
平台 必须逐平台验证裁剪、启动与商店审核要求
调试 真机日志、符号与异常栈需要保留

选择建议

HybridCLR 适合希望统一使用 C#、又需要接近原生 IL2CPP 运行方式的项目;ILRuntime 更偏向独立解释器与跨域绑定模型;xLua 等 Lua 方案则把热更边界放在脚本层。最终选择应基于团队语言能力、性能热点、包体、平台限制与发布节奏,而不是只看“谁更快”的单一指标。