解决 Unity 游戏中的 ANR 问题是一个系统化的流程:
集成报告服务
Android Vitals、Firebase Crashlytics 和 Backtrace(经过认证的 Unity 合作伙伴)等报告服务可为您的游戏提供大规模的错误日志记录和分析。在开发周期的早期将报告服务 SDK 集成到您的游戏中。分析哪种报告服务最符合您的游戏需求和预算。
不同的报告服务捕获 ANR 的方式不同。添加第二个报告服务,以提高获取有效数据的几率,为您修复 ANR 问题提供依据。
集成报告 SDK 不会影响游戏性能或 APK 大小。
分析符号
分析报告服务中的报告,并检查堆栈轨迹是否采用人类可读的格式。如需了解详情,请参阅对 Unity 游戏的 Android 崩溃和 ANR 问题进行符号化解析。
如何查看符号 build ID
如果报告系统显示 build ID 缺失,但 build 符号仍存在于 build 机存储空间中,则可以检查符号的 build ID,然后将其上传到报告服务。否则,您需要使用新的 build 上传符号文件。
在 Windows 或 macOS 上:
- 根据脚本后端导航到符号文件夹(请参阅解决方法):
- 使用以下命令(在 Windows 上,使用 Cygwin 运行
readelf
实用程序) - 您可以根据需要使用 Grep 过滤文本输出
- 查找 build ID
- 使用以下命令(在 Windows 上,使用 Cygwin 运行
readelf -n libil2cpp.so | grep 'Build ID'
Build ID: b42473fb7449e44e0182dd1f580c99bab0cd8a95
检查游戏代码
如果堆栈轨迹显示 libil2cpp.so
库中的函数,则表示错误发生在 C# 代码(已转换为 C++)中。libil2cpp.so
库不仅包含游戏代码,还包含插件和软件包。
C++ 文件名遵循 Unity 项目中定义的汇编名称。否则,文件名将采用默认的 Assembly-C# 名称。例如,图 3 显示了文件 Game.cpp
(以蓝色突出显示)的错误,该文件是汇编定义文件中定义的名称。Logger
是 C# 脚本中的类名称(以红色突出显示),后跟函数名称(以绿色突出显示)。最后是 IL2CPP 转换器生成的完整名称(以橙色突出显示)。
执行以下操作,检查游戏代码:
- 检查 C# 项目,看是否存在任何可疑代码。通常,C# 未处理的异常不会导致 ANR 或应用崩溃。即便如此,也要确保代码在不同情况下都能正常运行。检查代码是否使用了第三方引擎模块,并分析近期的版本是否引入了错误。此外,请检查您是否最近更新了 Unity,或者错误是否仅在特定设备上发生。
- 将游戏导出为 Android Studio 项目。如果您拥有对游戏转换后的 C# 源代码的完全访问权限,就可以找到导致 ANR 的函数。C++ 代码与 C# 代码看起来截然不同,并且代码转换很少会出现问题。如果您发现了问题,请向 Unity 提交支持服务工单。
- 查看游戏源代码,确保适当清理在 OnApplicationFocus() 和 OnApplicationPause() 回调中运行的任何逻辑。
- Unity 引擎具有用于暂停其执行的超时设置;如果这些回调的工作负载过多,可能会导致 ANR。
- 向代码的各个部分添加日志或面包屑导航可增强数据分析。
- 使用 Unity 性能分析器来调查游戏的性能。对应用进行性能分析也是帮助您找出可能导致 ANR 的瓶颈的绝佳方式。
- 若要识别主线程上的长时间 I/O 操作,一个很好的方法是使用严格模式。
- 分析 Android Vitals 或其他报告服务历史记录,并检查出现错误最多的游戏的发布版本。在版本控制历史记录中查看源代码,并比较各个版本之间的代码更改。如果您发现可疑内容,请逐一尝试每次更改或可能的解决方法。
- 检查收到 ANR 最多的设备和 Android 版本的 Google Play ANR 报告历史记录。如果设备或版本已过时,如果这样做不会影响游戏的盈利能力,您很可能可以放心地忽略它们。请仔细研究数据,因为某一组用户将无法再玩您的游戏。如需了解详情,请参阅分发信息中心。
- 查看游戏源代码,确保您没有调用任何可能导致问题的代码,例如,如果使用不当,finish 可能会具有破坏性。如需详细了解 Android 开发,请参阅 Android 开发者指南。
- 查看数据并将游戏 build 导出到 Android Studio 后,您将处理 C 和 C++ 代码,因此可以充分利用 Unity 标准解决方案之外的工具,例如 Android 内存性能分析器、Android CPU 性能分析器和 perfetto。
Unity 引擎代码
如需了解 Unity 引擎端是否发生了 ANR,请在堆栈轨迹中检查是否存在 libUnity.so
或 libMain.so
。如果您发现此类问题,请按以下步骤操作:
- 首先,搜索社区渠道(Unity 论坛、Unity 讨论、Stackoverflow)。
- 如果您没有找到任何内容,请提交 bug 以解决问题。提供经过符号化解析的堆栈轨迹,以便引擎的工程师可以更好地了解和解决错误。
- 检查最新的 Unity LTS 是否针对您的问题进行了改进。如果是,请升级您的游戏以使用该版本。(此解决方案可能仅适用于某些开发者。)
- 如果您的代码使用自定义
Activity
而非默认Activity
,请检查 Java 代码,确保 activity 不会导致任何问题。
第三方 SDK
- 检查所有第三方库是否为最新版本,并且没有针对最新版 Android 的崩溃或 ANR 报告。
- 请访问 Unity 论坛,了解是否有任何错误已在较高版本中得到解决,或者 Unity 或社区成员是否提供了解决方法。
- 查看 Google Play ANR 报告,确保 Google 尚未发现该错误。Google 已经意识到了一些 ANR 问题,正在积极解决。
系统库
系统库通常不受开发者的控制,但它们所占 ANR 比例不高。除了与库开发者联系或添加日志以缩小问题范围之外,系统库 ANR 很难解决。
退出原因
ApplicationExitInfo
是一个 Android API,用于了解 ANR 原因。如果您的游戏使用的是 Unity 6 或更高版本,您可以直接调用 ApplicationExitInfo
。对于较低版本的 Unity,您需要实现自己的插件,才能从 Unity 启用 ApplicationExitInfo
调用。
Crashlytics 也使用 ApplicationExitInfo
;不过,您可以通过自己的实现实现更精细的控制,并添加更相关的信息。