热更新系列(二)——HybridCLR(huatuo)
关于HybridCLR
[官网](HybridCLR | HybridCLR (code-philosophy.com))
HybridCLR改进了unity的Il2cpp部分,使得其能”解释”执行一些代码,每个unity都可以在安装文件下找到\Data\il2cpp\libil2cpp
, HybridCLR就是改进了这一部分.
具体查看:安装HybridCLR | HybridCLR (code-philosophy.com)
原始il2cpp是AOT运行时,不支持动态注册dll元数据。我们轻微改造了metadata管理模块,插入了一些hook代码,支持动态加载dll元数据
安装
依赖模块
- Unity Hub 安装相应的IL2cpp模块
- Windows
- Win下需要安装
visual studio 2019
或更高版本。安装时至少要包含使用Unity的游戏开发
和使用c++的游戏开发
组件。 - 安装git
- Win下需要安装
- Mac
- 要求MacOS版本 >= 12,xcode版本 >= 13,例如
xcode 13.4.1, macos 12.4
。 - 安装 git
- 安装cmake
- 要求MacOS版本 >= 12,xcode版本 >= 13,例如
目录说明
AssembliesPostIl2CppStrip
打包过程生成的裁剪后的AOT dll. 用于补充元数据,分为不同平台,不同平台不可混用,至于为什么是剪裁后的AOTdll,这是为了减小生成的dll大小也是为了减小内存(下同)
HotUpdateDlls
生成的热更程序集
hybridclr_repo
Hybridclr仓库代码.扩充了il2cpp的代码,使它由纯AOT runtime变成‘AOT+Interpreter’ 混合runtime,进而原生支持动态加载assembly,使得基于il2cpp backend打包的游戏不仅能在Android平台,也能在IOS、Consoles等限制了JIT的平台上高效地以AOT+interpreter混合模式执行,从底层彻底支持了热更新。为解释器部分的核心代码,包含元数据加载、代码transform(编译)、代码解释执行
il2cpp_plus_repo
il2cpp_plus仓库代码,原始的
libil2cpp
是AOT运行,Hybirdclr改造了metadata管理模块,插入一些hook代码,支持动态加载dll元数据LocalIl2CppData-WindowsEditor
这里是把Unity自带的
MonoBleedingEdge(2019以上)+Il2cpp+改造后的libil2cpp
注:每个平台Editor自带il2cpp不同(windows.linux,macos…),所有要区分
StrippedAOTDllsTempProj
生成剪裁后的AOTdll临时工程(意思就是要生成剪裁后的AOTdll得有个工程)
Unity HybridSetting
关于AOTGenericReferences
HybridCLR/generate/AOTGenericReference
命令生成的AOTGenericReferences.cs
文件中包含了应该补充元数据的assembly列表.分为三部分
- 1是需要补充的元数据assembly
- 2是AOT中用到的泛型类
- 3是AOT中用到的泛型方法
遇到的一些问题和注意事项
IL2CPP泛型共享代码
il2cpp为了避免泛型代码膨胀,节约内存,在保证代码逻辑正确性的情况下对于一些能够共享代码,只生成一份代码。为此引入一个概念叫泛型代码共享 Generic Sharing。 简单来说,你只要在AOT中实例化过某个泛型类或泛型函数的共享实例,你就可以在热更新代码中使用它了。
以List
举例:
- 可以使用AOT中使用过的任何List的实例化类型。例如你在AOT里用过List
,则热更新里也可以用 - 可以使用任意List
。 只需要你在AOT里实例化某一个List<相同underlying type的枚举类型>。 - 可以使用任意引用类型的泛型参数List
。 只需要你在AOT里实例化过 List
注意最后一条,只要实例化一个了任意引用类型,那么在热更中可以不用补充元数据
基于补充元数据的泛型函数实例化技术hybridclr的专利技术
热更入口函数的坑
如上图所示,如果直接在入口函数调用了泛型操作,那么会报错,报错原因就是没找到元数据
那么为什么会出现这个问题呢,因为已经补充元数据了
这是因为hybridclr是以methodbody
解释执行的,也就是整ge解释属于这个方法里面内容,例如start方法
里面如果包含了泛型,先整个解释这个start方法,发现没有补充元数据所以就报错了,直接log函数都不打印
解决办法
就是用个方法包裹一层,先补充元数据,后面就没啥问题了