解决 Unity 游戏中 ANR 的过程是一个系统化的过程:
![](https://developer.android.google.cn/static/images/games/engines/unity/unity-anr-flowchart.jpg?hl=zh-cn)
集成报告服务
Android Vitals、Firebase Crashlytics 和 Backtrace(经过认证的 Unity 合作伙伴)等报告服务可针对您的游戏进行大规模的错误日志记录和分析。在开发周期的早期阶段,将报告服务 SDK 集成到游戏中。分析哪种报告服务最适合您的游戏需求和预算。
不同的报告服务采用不同的方式捕获 ANR。添加第二个报告服务,以提高获取有效数据的几率,以支持您解决 ANR 问题的决策。
集成报告 SDK 不会影响游戏性能或 APK 大小。
分析符号
分析报告服务的报告,并检查堆栈轨迹是否采用人类可读懂的格式。如需了解详情,请参阅对 Unity 游戏的 Android 崩溃和 ANR 问题进行符号化解析。
![](https://developer.android.google.cn/static/images/games/engines/unity/unity-anr-crashlytics.png?hl=zh-cn)
libil2cpp.so
符号。如何查看符号 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 转换器生成的全名(以橙色突出显示)。
![](https://developer.android.google.cn/static/images/games/engines/unity/unity-anr-backtrace.png?hl=zh-cn)
执行以下操作,检查游戏代码:
- 检查 C# 项目中是否存在任何可疑代码。通常,C# 未处理的异常不会导致 ANR 或应用崩溃。即便如此,也要确保代码在不同情况下都能正常运行。请检查代码是否使用了第三方引擎模块,并分析最新版本是否引入了错误。此外,请检查您最近是否更新了 Unity,或者错误是否仅发生在特定设备上。
- 将游戏导出为 Android Studio 项目。您可以完全访问游戏转换后的 C# 源代码,以找到导致 ANR 的函数。C++ 代码看起来与 C# 代码截然不同,而且代码转换很少会出问题。如果您确实发现问题,可以向 Unity 提交支持请求。
- 查看游戏源代码,并确保正确清理 OnApplicationFocus() 和 OnApplicationPause() 回调中运行的任何逻辑。
- Unity 引擎设有暂停执行的超时时间;对这些回调的过多工作负载可能会导致 ANR。
- 向部分代码添加日志或面包屑导航,以增强数据分析。
- 使用 Unity Profiler 调查游戏的性能。对应用进行性能剖析也是一种有助于确定可能导致 ANR 的瓶颈的好方法。
- 如需确定主线程上的长时间 I/O 操作,不妨使用严格模式。
- 分析 Android Vitals 或其他报告服务历史记录,并检查最常出错的游戏的发布版本。查看版本控制历史记录中的源代码,并比较不同版本之间的代码更改。如果您发现可疑情况,请分别尝试每项更改或潜在解决方法。
- 查看发生 ANR 最多的设备和 Android 版本的 Google Play ANR 报告历史记录。如果设备或版本已过时,您可以放心地忽略它们,前提是这样做不会影响游戏的盈利能力。请认真研究相关数据,因为特定的用户群体将无法再玩您的游戏。如需了解详情,请参阅分发信息中心。
- 查看游戏源代码,确保您没有调用任何可能导致问题的代码,例如,如果使用正确,finish 具有破坏性。如需详细了解 Android 开发,请参阅 Android 开发者指南。
- 查看数据并将游戏 build 导出到 Android Studio 后,您需要处理 C 和 C++ 代码,因此,您可以充分利用 Unity 标准解决方案以外的工具,例如 Android 内存分析器、Android CPU 性能分析器和 perfetto。
Unity 引擎代码
如需了解 ANR 是否发生在 Unity 引擎端,请检查堆栈轨迹中是否存在 libUnity.so
或 libMain.so
。如果找到这些文件,请按以下步骤操作:
- 首先,搜索社区频道(Unity 论坛、Unity 论坛、Stackoverflow)。
- 如果您找不到任何结果,请提交 bug 来解决问题。提供经过符号化解析的堆栈轨迹,以便引擎的工程师能够更好地了解和解决错误。
- 检查最新的 Unity LTS 是否对您的问题进行了改进。如果是这样,请升级游戏以使用该版本。(此解决方案可能仅适用于部分开发者。)
- 如果您的代码使用自定义
Activity
而非默认值,请查看 Java 代码以确保相应 activity 不会导致任何问题。
第三方 SDK
- 检查所有第三方库是否均为最新版本,并且没有针对最新版 Android 的崩溃或 ANR 报告。
- 请访问 Unity 论坛,查看是否有任何错误已在更高版本中得到解决,或者 Unity 或社区成员是否提供了解决方法。
- 查看 Google Play ANR 报告,确保 Google 尚未发现错误。Google 已注意到一些 ANR,并且正在积极解决。
系统库
系统库通常远离开发者的控制范围,但它们在 ANR 问题的比例中不占很大比例。除了联系库开发者或添加日志来缩小问题范围之外,系统库 ANR 还难以解决。
退出原因
ApplicationExitInfo
是一个用于了解 ANR 原因的 Android API。如果您的游戏使用的是 Unity 6 或更高版本,您可以直接调用 ApplicationExitInfo
。对于较低版本的 Unity,您需要实现自己的插件,才能从 Unity 启用 ApplicationExitInfo
调用。
Crashlytics 还使用 ApplicationExitInfo
;但是,您自己的实现方式可让您进行更精细的控制,并允许您包含相关性更高的信息。