当前位置:首页 > 编程开发

Unity + HybridCLR 社区版下 UniTask 泛型 AOT 问题排查记录

最近在接入小七 GameSDK 登录流程时,Android 真机上遇到了 HybridCLR + UniTask 的 AOT 泛型缺失问题。这里记录一下问题现象、排查过程和最终处理方案。

问题现象

真机日志里出现类似错误:

AOT generic method not instantiated in aot.
assembly: UniTask.dll
method:
Cysharp.Threading.Tasks.UniTask`1[
  System.ValueTuple`2[System.Boolean,GameSDK.LoginResult]
]
Cysharp.Threading.Tasks.UniTask::WhenAny<GameSDK.LoginResult>(
  UniTask<LoginResult>,
  UniTask
)

最开始还出现过:

UniTaskCompletionSource<ValueTuple<bool, string>>::get_Task()
TrySetResult(ValueTuple<bool, string>)

这些都属于典型的 HybridCLR AOT 泛型问题。

根因

项目使用的是 HybridCLR 社区版,不是商业版完全泛型共享。

社区版下,如果 AOT 程序集里的泛型方法在 IL2CPP 编译阶段没有生成对应实例,热更层运行时调用就可能报:

AOT generic method not instantiated

这次缺失的方法在日志里已经写得很清楚:

assembly: UniTask.dll
UniTask.WhenAny<LoginResult>

所以真正缺的是 UniTask.dll 的补充元数据,而不是热更 DLL 本身。

一开始走过的弯路

我们尝试过在 RefTypes.cs 里补泛型引用,例如:

RefUniTaskCompletionSource(default(LoginResult));
RefUniTaskResult(default(ValueTuple<bool, LoginResult>));

LoginResult 在热更程序集里,RefTypes.cs 属于 AOT 侧。直接在 AOT 侧引用热更类型,容易触发 linker 问题,比如之前遇到的 IL1005

所以最终不建议在 RefTypes 里直接 using GameSDK 或直接引用 LoginResult

正确方向

根据 HybridCLR 文档,社区版可以通过补充元数据解决 AOT 泛型问题。

这次报错来自:

UniTask.dll

因此需要把 UniTask 加入 AOT 补充元数据列表,并确保运行时加载:

Assets/GameMain/AotDll/UniTask.bytes

实际修改

1. 运行时 AOT 元数据加载列表

在:

Assets/Scripts/Procedure/ProcedureLoadHotFixDll.cs

UniTask 加入 aotDllList

public static List<string> aotDllList = new List<string>
{
    "Base",
    "GameFramework",
    "Google.Protobuf",
    "System",
    "UniTask",
    "UnityGameFramework.Runtime",
    "mscorlib",
};

运行时会通过:

RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, HomologousImageMode.SuperSet);

加载这些 AOT 元数据。

2. HybridCLR 配置

在:

ProjectSettings/HybridCLRSettings.asset

UniTask 加入 patchAOTAssemblies

否则执行 HybridCLR 的复制流程时,不会自动把 UniTask.dll 拷贝成 UniTask.bytes

3. 编辑器构建列表

在:

Assets/ThirdParty/HybridCLR/Editor/BuildConfig.cs

也加入:

"UniTask",

保证项目自定义构建 AOT bytes 时也会处理 UniTask。

4. 生成 AOT bytes

项目里 Assets/GameMain/AotDll 的命名规则是:

程序集名.bytes

不是:

程序集名.dll.bytes

所以最终应该存在:

Assets/GameMain/AotDll/UniTask.bytes

来源是:

HybridCLRData/AssembliesPostIl2CppStrip/Android/UniTask.dll

RefTypes 最终怎么处理

RefTypes.cs 保持基础 UniTask 泛型引用即可,不直接引用热更类型:

RefUniTaskCompletionSource(default(bool));
RefUniTaskCompletionSource(default(string));
RefUniTaskCompletionSource(default(object));
RefUniTaskCompletionSource(default(ValueTuple<bool, string>));
RefUniTaskCompletionSource(default(ValueTuple<bool, object>));

RefUniTaskResult(default(object));
RefUniTaskResult(default(ValueTuple<bool, object>));

LoginResult 这种热更类型的泛型问题,交给 UniTask.bytes 补充元数据解决。

登录流程调整

另一个问题是登录流程本身。

最终约定:

  • initSdk() 只做 SDK 初始化

  • 不在 initSdk() 里触发登录

  • 登录只在 SendwxLogin() 里触发

  • 第一次进入登录界面不自动登录

  • 用户点击登录按钮后再走小七登录

原因是 initSdk() 会被 UI 刷新、协议同意等多处调用。如果把登录放进去,登录失败后刷新 UI 可能导致反复自动登录。

小七登录超时问题

原来登录服务里有 15 秒超时:

var loginTask = _provider.LoginAsync();
var timeoutTask = UniTask.Delay(TimeSpan.FromSeconds(15));
var (hasResult, loginResult) = await UniTask.WhenAny(loginTask, timeoutTask);

这个对小七不合理。

小七登录会弹账号密码输入框,用户可能超过 15 秒才输入。这个场景不应该由客户端 SDK 层强制超时。

最终改为直接等待 SDK 回调:

var loginResult = await _provider.LoginAsync();

小七 Provider 内部也改成:

var loginResult = await _loginTcs.Task;

只有 SDK 返回成功、失败或取消时,登录流程才继续。

内存影响

补充 UniTask.bytes 会增加包体和运行时内存。

当前 UniTask.bytes 大约 512 KB。加载补充元数据后,运行时内存会有额外占用,而且这部分通常不可回收。

如果对内存敏感,可以考虑使用 HybridCLR 提供的补充元数据裁剪工具,优化 AOT metadata dll 大小。

总结

这次问题的关键点是:

  1. 报错看 assembly,这次是 UniTask.dll

  2. 社区版要靠补充元数据解决 AOT 泛型

  3. UniTask 必须加入 AOT 元数据列表

  4. 不建议在 RefTypes 里直接引用热更类型

  5. initSdk() 只初始化,登录放在用户触发的登录入口

  6. 小七账号密码登录不能加固定 15 秒超时

最终方案是:

业务代码正常写 UniTask 泛型
+
运行时加载 UniTask.bytes 补充元数据
+
RefTypes 只保留基础泛型引用

这套更符合 HybridCLR 社区版的使用方式。


扫描二维码推送至手机访问。

版权声明:本文由知了博客发布,如需转载请注明出处。

本文链接:https://www.webgou.info/?id=702

标签: unityAI
分享给朋友:

“Unity + HybridCLR 社区版下 UniTask 泛型 AOT 问题排查记录” 的相关文章

四大图像库:OpenCV/FreeImage/CImg/CxImage .

 本文转载自:http://hi.baidu.com/xiaocuiman/blog/item/6e267c2bc4b1883f5243c108.html...…

Webservice 的设计和模式

本文是篇译文(原文在devx),对于想初步了解webservice的朋友可能有些帮助。http://www.devx.com/enterprise/Article/10397http://www.cnblogs.com/idior/archive/2004/12/13/76600.html...…

TCPMP分析

 TCPMP是一个功能强大开放式的开源多媒体播放器,播放器主…

ECC

ECC…

5 Ways to Make HTTP Requests in Node.js

 HTTP – the Standard LibraryFirst on our hit parade is the default HTTP module in the standard library. With th…

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。