调试 ANR

解决 Unity 游戏中的 ANR 是一个系统化的过程

图 1. 解决 Unity 游戏中 ANR 的步骤。

集成报告服务

诸如 Android vitalsFirebase CrashlyticsBacktrace(经过认证的 Unity 合作伙伴)之类的报告服务可以大规模地为你的游戏提供错误日志记录和分析。在开发周期的早期将报告服务 SDK 集成到你的游戏中。分析哪个报告服务最符合你的游戏需求和预算。

不同的报告服务捕获 ANR 的方式不同。包含第二个报告服务以增加获得有效数据以支持你修复 ANR 决策的机会。

集成报告 SDK 不会影响游戏性能或 APK 大小。

分析符号

分析你的报告服务的报告,并检查堆栈跟踪是否为人类可读格式。有关更多信息,请参阅 为 Unity 游戏符号化 Android 崩溃和 ANR

图 2. Crashlytics 显示构建 ID 和缺少的 libil2cpp.so 符号。

如何检查符号构建 ID

如果报告系统显示缺少构建 ID,但构建符号仍然存在于构建机器存储中,则可以检查符号的构建 ID,然后将其上传到报告服务。否则,需要新的构建才能上传符号文件。

在 Windows 或 macOS 上

  1. 根据你的 脚本后端(参见**解决方案**:)导航到符号文件夹。
    1. 使用以下命令(在 Windows 上,使用 Cygwin 运行 readelf 实用程序)
    2. Grep 使用是可选的,用于过滤文本输出
    3. 查找构建 ID
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 转换器生成的完整名称(橙色突出显示)。

图 3. 来自 Backtrace 的测试项目调用堆栈。

通过执行以下操作检查你的游戏代码

  • 检查 C# 项目中是否有任何可疑代码。通常,C# 未处理的异常不会导致 ANR 或应用程序崩溃。即便如此,也要确保代码在不同情况下都能正常运行。检查代码是否使用了第三方引擎模块,并分析最近的版本是否引入了错误。此外,请检查你是否最近更新了 Unity,或者错误是否只发生在特定设备上。
  • 将游戏导出为 Android Studio 项目。通过完全访问你游戏转换后的 C# 源代码,你可以找到导致 ANR 的函数。C++ 代码看起来与你的 C# 代码非常不同,代码转换很少出现问题。如果你确实发现了问题,请向 Unity 提交支持工单。
  • 查看游戏源代码,并确保在 OnApplicationFocus()OnApplicationPause() 回调中运行的任何逻辑都已适当地清理。
    • Unity 引擎有一个超时来暂停其执行;这些回调上的工作量过大可能会导致 ANR。
    • 向代码部分添加日志或面包屑以增强你的数据分析。
  • 使用 Unity Profiler 来调查游戏的性能。分析你的应用程序也可以帮助识别可能导致 ANR 的瓶颈。
  • 识别主线程上长时间 I/O 操作的一个好方法是使用 严格模式
  • 分析 Android Vitals 或其他报告服务的历史记录,并检查发生错误最多的游戏的发布版本。查看版本控制历史记录中的源代码,并比较不同版本之间的代码更改。如果你发现可疑之处,请分别尝试每个更改或潜在的修复。
  • 检查 Google Play ANR 报告历史记录中接收最多 ANR 的设备和 Android 版本。如果设备或版本已过时,则如果这样做不会影响游戏的盈利能力,则可以安全地忽略它们。仔细研究数据,因为特定用户组将无法再玩你的游戏。有关更多信息,请参阅 分发控制面板
  • 查看游戏源代码,确保你没有调用任何可能导致问题的代码,例如,如果 finish 使用不当,可能会造成破坏。请参阅 Android 开发者指南 了解更多关于 Android 开发的信息。
  • 在查看数据并将游戏构建导出到 Android Studio 后,你将处理 C 和 C++ 代码,因此你可以充分利用 Unity 标准解决方案之外的工具,例如 Android 内存分析器Android CPU 分析器perfetto

Unity引擎代码

要确定ANR是否发生在Unity引擎端,请检查堆栈跟踪中是否有libUnity.solibMain.so。如果找到它们,请执行以下步骤

  • 首先,搜索社区渠道(Unity论坛Unity讨论Stack Overflow)。
  • 如果找不到任何信息,提交错误报告以解决问题。提供符号化的堆栈跟踪,以便引擎工程师能够更好地理解和解决错误。
  • 检查最新的Unity LTS版本是否对您的问题进行了改进。如果是,请升级您的游戏以使用该版本。(此解决方案可能仅适用于部分开发者。)
  • 如果您的代码使用自定义的Activity而不是默认的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;但是,您自己的实现可以让您更精细地控制,并使您能够包含更多相关信息