当 AOT 代码中已经使用过某泛型声明时,热更DLL中可以直接使用这种泛型代码。例如,当 AOT 代码中已经使用过 List<float> 时,在热更代码中才能使用 List<float> 泛型,否则会报错。
我们用上一章节使用过的测试用例来验证这一点。
using UnityEngine;
public class Hello
{
public static void Run()
{
Debug.Log("Hello, World");
}
}
运行测试
修改测试用例
using System.Collections.Generic;
using UnityEngine;
public class Hello
{
public static void Run()
{
Debug.Log("Hello, World");
var arr = new List<float>();
arr.Add(2.0f);
}
}
执行菜单:【HybridCLR】->CompileDll->ActiveBuildTarget 重新生成热更DLL,并替换掉发布程序中的 HotUpdate.dll.bytes 文件。
再次运行测试
从下图中的报错信息可以看出,当 AOT 代码中没使用过 List<float> 时,热更代码使用了 List<float> 会引起报错。这是因为 AOT 在编译时未记录 List<float> 的元数据导致。
执行菜单 【HybridCLR】->Generate->AOTDlls 重新生成包含元数据的 AOT DLL。
重新生成的 AOT DLL 自动保存在 {工程路径}\HybridCLRData\AssembliesPostIl2CppStrip\{平台目录} 下面。
将 mscorlib.dll 改名为 mscorlib.dll.bytes 并拷贝到发布程序的 StreamingAssets 下面,如图:
mscorlib.dll.bytes 也需要动态加载,我们接下来对上一章节中的用例代码进行修改。
using System;
using System.Reflection;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using HybridCLR;
public class LoadDll : MonoBehaviour
{
void Start()
{
// 先补充元数据
LoadMetadataForAOTAssemblies();
// Editor环境下,HotUpdate.dll.bytes已经被自动加载,不需要加载,重复加载反而会出问题。
#if !UNITY_EDITOR
Assembly hotUpdateAss = Assembly.Load(File.ReadAllBytes($"{Application.streamingAssetsPath}/HotUpdate.dll.bytes"));
#else
// Editor下无需加载,直接查找获得HotUpdate程序集
Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");
#endif
Type type = hotUpdateAss.GetType("Hello");
type.GetMethod("Run").Invoke(null, null);
}
private static void LoadMetadataForAOTAssemblies()
{
List<string> aotDllList = new List<string>
{
"mscorlib.dll",
"System.dll",
//如果使用了Linq,需要这个
"System.Core.dll",
//"Newtonsoft.Json.dll",
//"protobuf-net.dll",
};
foreach (var aotDllName in aotDllList)
{
string filePath = $"{Application.streamingAssetsPath}/{aotDllName}.bytes";
if (!File.Exists(filePath))
continue;
byte[] dllBytes = File.ReadAllBytes(filePath);
LoadImageErrorCode err = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, HomologousImageMode.SuperSet);
Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}");
}
}
}
因为 LoadDll.cs 代码在 AOT 程序集中,需要重新 Build 一个包。现在热更流程变成了先加载元数据DLL,再加载需要热更的逻辑DLL。现在如果在热更代码中新增了 AOT DLL 中没使用到的泛型,只需重新生成 AOT DLL 并丢在 StreamingAssets 下,即可完成补充元数据操作。
再次运行测试。可以看到,补充了元数据后,List<float> 不再报错。
通过 【HybridCLR】->Generate->AOTDlls 生成的 AOD DLL 包含了很多没用到的元数据,接下来写个自定义菜单用来剥离未使用到的元数据。
新创建个 HybridCLREditor.cs 并放到 Assets/Editor 目录下。
using System.IO;
using UnityEditor;
using HybridCLR.Editor;
using HybridCLR.Editor.AOT;
public class HybridCLREditor
{
// 进一步剔除AOT dll中非泛型函数元数据,输出到StrippedAOTAssembly2目录下
[MenuItem("HybridCLR/Strip AOT Assembly")]
public static void StripAOTAssembly()
{
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
string srcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
string dstDir = $"{SettingsUtil.HybridCLRDataDir}/StrippedAOTAssembly2/{target}";
foreach (var src in Directory.GetFiles(srcDir, "*.dll"))
{
string dllName = Path.GetFileName(src);
string dstFile = $"{dstDir}/{dllName}";
AOTAssemblyMetadataStripper.Strip(src, dstFile);
}
}
}
执行完 【HybridCLR】->Generate->AOTDlls 后,再执行 【HybridCLR】->Strip AOT Assembly 菜单,即可生成优化后的 AOT DLL。优化后的 AOT DLL 存放在 HybridCLRData\StrippedAOTAssembly2\{平台目录} 中。
查看优化前和优化后的大小差异。
优化前大小 1.66MB。
优化后大小 740KB。
在 “Project Settings -> Other Settings -> IL2CPP Code Generation” 中可设置泛型共享机制。
Faster runtime:标准泛型共享机制,性能高,包体大。(HybridCLR官方建议启用 Faster runtime)
Faster (smaller) builds:完全泛型共享机制,性能低,包体小。