解决 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 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
;但是,您自己的实现方式可让您进行更精细的控制,并允许您包含相关性更高的信息。