使用高级 Crashlytics 功能了解 Unity 游戏的崩溃

一、简介

在此 Codelab 中,您将学习如何使用 Crashlytics 的高级功能,这将使您更好地了解崩溃以及可能导致崩溃的情况。

您将为示例游戏MechaHamster: Level Up with Firebase Edition添加新功能。此示例游戏是经典 Firebase 游戏 MechaHamster 的新版本,删除了大部分内置 Firebase 功能,让您有机会在原来的位置实现 Firebase 的新用途。

您将向游戏添加调试菜单。此调试菜单调用您将创建的方法,并允许您练习 Crashlytics 的不同功能。这些方法将向您展示如何使用自定义键、自定义日志、非致命错误等来注释自动崩溃报告。

构建游戏后,您将使用调试菜单,并检查结果以了解它们为您的游戏在野外运行方式提供的独特视图。

你将学到什么

  • Crashlytics 自动捕获的错误类型。
  • 可以有目的地记录的其他错误。
  • 如何向这些错误添加更多信息以使它们更容易理解。

你需要什么

  • Unity(最低推荐版本 2019+)具有以下一项或两项:
    • iOS 构建支持
    • Android 构建支持
  • (仅适用于 Android) Firebase CLI(用于上传崩溃报告的符号)

2. 设置您的开发环境

以下部分介绍如何下载Level Up with Firebase代码并在 Unity 中打开它。

请注意,此Level Up with Firebase示例游戏已被其他几个 Firebase + Unity Codelab 使用,因此您可能已经完成了本部分中的任务。如果是这样,您可以直接转到此页面的最后一步:“为 Unity 添加 Firebase SDK”。

下载代码

从命令行克隆此 Codelab 的GitHub 存储库

git clone https://github.com/firebase/level-up-with-firebase.git

或者,如果您没有安装 git,则可以将存储库下载为 ZIP 文件

在 Unity 编辑器中使用 Firebase 打开 Level Up

  1. 启动 Unity Hub,然后从“项目”选项卡中单击“打开”旁边的下拉箭头
  2. 单击从磁盘添加项目
  3. 导航到包含代码的目录,然后单击“确定”
  4. 如果出现提示,请选择要使用的 Unity 编辑器版本以及您的目标平台(Android 或 iOS)。
  5. 单击项目名称level-up-with-firebase ,该项目将在 Unity 编辑器中打开。
  6. 如果您的编辑器没有自动打开它,请在 Unity 编辑器的“项目”选项卡中的“资源” > “Hamster”中打开MainGameScene
    ff4ea3f3c0d29379.png

有关安装和使用 Unity 的更多信息,请参阅在 Unity 中工作

3. 将 Firebase 添加到您的 Unity 项目

创建 Firebase 项目

  1. Firebase 控制台中,单击添加项目
  2. 要创建新项目,请输入所需的项目名称。
    这还将根据项目名称将项目 ID(显示在项目名称下方)设置为某些内容。您可以选择单击项目 ID 上的编辑图标以进一步自定义它。
  3. 如果出现提示,请查看并接受Firebase 条款
  4. 单击继续
  5. 选择为此项目启用 Google Analytics选项,然后单击继续
  6. 选择要使用的现有 Google Analytics 帐户,或选择创建新帐户来创建新帐户。
  7. 单击创建项目
  8. 创建项目后,单击“继续”

向 Firebase 注册您的应用

  1. 仍然在Firebase 控制台中,从项目概述页面的中心,单击 Unity 图标以启动设置工作流程,或者,如果您已将应用添加到 Firebase 项目,请单击添加应用以显示平台选项。
  2. 选择注册 Apple (iOS) 和 Android 构建目标。
  3. 输入 Unity 项目的平台特定 ID。对于此 Codelab,请输入以下内容:
    • 对于 Apple (iOS) :在iOS 捆绑包 ID字段中输入com.google.firebase.level-up
    • 对于 Android :在Android 包名称字段中输入com.google.firebase.level_up
  4. (可选)输入 Unity 项目的平台特定昵称。
  5. 单击注册应用程序,然后转到下载配置文件部分。

添加 Firebase 配置文件

单击Register app后,系统将提示您下载两个配置文件(每个构建目标一个配置文件)。您的 Unity 项目需要这些文件中的 Firebase 元数据才能与 Firebase 连接。

  1. 下载两个可用的配置文件:
    • 对于 Apple (iOS) :下载GoogleService-Info.plist
    • 对于 Android :下载google-services.json
  2. 打开 Unity 项目的“项目”窗口,然后将两个配置文件移动到Assets文件夹中。
  3. 返回 Firebase 控制台的设置工作流程中,单击下一步并继续添加适用于 Unity 的 Firebase SDK。

添加适用于 Unity 的 Firebase SDK

  1. 单击 Firebase 控制台中的“下载 Firebase Unity SDK”
  2. 将 SDK 解压到方便的地方。
  3. 在打开的 Unity 项目中,导航至Assets > Import Package > Custom Package
  4. “导入包”对话框中,导航到包含解压缩的 SDK 的目录,选择FirebaseAnalytics.unitypackage ,然后单击“打开”
  5. 在出现的“导入 Unity 包”对话框中,单击“导入”
  6. 重复前面的步骤以导入FirebaseCrashlytics.unitypackage
  7. 返回 Firebase 控制台,然后在设置工作流程中单击“下一步”

有关将 Firebase SDK 添加到 Unity 项目的更多信息,请参阅其他 Unity 安装选项

4. 在 Unity 项目中设置 Crashlytics

要在 Unity 项目中使用 Crashlytics,您需要执行更多设置步骤。当然,您需要初始化SDK。而且,您还需要上传符号,以便可以在 Firebase 控制台中看到符号化的堆栈跟踪,并且需要强制测试崩溃以确保 Firebase 获取崩溃事件。

初始化 Crashlytics SDK

  1. Assets/Hamster/Scripts/MainGame.cs中,添加以下using语句:
    using Firebase.Crashlytics;
    using Firebase.Extensions;
    
    第一个模块允许您使用 Crashlytics SDK 中的方法,第二个模块包含C# Tasks API的一些扩展。如果没有这两个using语句,下面的代码将无法工作。
  2. 仍然在MainGame.cs中,通过调用InitializeFirebaseAndStartGame()将 Firebase 初始化添加到现有Start()方法:
    void Start()
    {
      Screen.SetResolution(Screen.width / 2, Screen.height / 2, true);
      InitializeFirebaseAndStartGame();
    }
    
  3. 再次,在MainGame.cs中,找到InitializeFirebaseAndStartGame() ,声明一个 app 变量,然后覆盖该方法的实现,如下所示:
    public Firebase.FirebaseApp app = null;
    
    // Begins the firebase initialization process and afterwards, opens the main menu.
    private void InitializeFirebaseAndStartGame()
    {
      Firebase.FirebaseApp.CheckAndFixDependenciesAsync()
      .ContinueWithOnMainThread(
        previousTask => 
        {
          var dependencyStatus = previousTask.Result;
          if (dependencyStatus == Firebase.DependencyStatus.Available) {
            // Create and hold a reference to your FirebaseApp,
            app = Firebase.FirebaseApp.DefaultInstance;
            // Set the recommended Crashlytics uncaught exception behavior.
            Crashlytics.ReportUncaughtExceptionsAsFatal = true;
            InitializeCommonDataAndStartGame();
          } else {
            UnityEngine.Debug.LogError(
              $"Could not resolve all Firebase dependencies: {dependencyStatus}\n" +
              "Firebase Unity SDK is not safe to use here");
          }
        });
    }
    

将初始化逻辑放置在此处可防止在 Firebase 依赖项初始化之前玩家进行交互。

Crashlytics 常见问题解答中讨论了将未处理的异常报告为致命的好处和影响。

构建您的项目并上传符号

iOS 和 Android 应用程序的构建和上传符号的步骤不同。

iOS+(苹果平台)

  1. “构建设置”对话框中,将项目导出到 Xcode 工作区。
  2. 构建您的应用程序。
    对于 Apple 平台,Firebase Unity Editor 插件会自动配置您的 Xcode 项目,以便为每个构建生成兼容 Crashlytics 的符号文件并将其上传到 Firebase 服务器。需要此符号信息才能在 Crashlytics 仪表板中查看符号化堆栈跟踪。

安卓

  1. (仅在初始设置期间,不适用于每个构建)设置您的构建:
    1. 在项目目录的根目录下创建一个名为Builds的新文件夹(即,作为Assets目录的同级文件夹),然后创建一个名为Android的子文件夹。
    2. File > Build Settings > Player Settings > Configuration中,将 Scripting Backend 设置为 IL2CPP。
      • IL2CPP 通常会导致构建更小并具有更好的性能。
      • IL2CPP 也是 iOS 上唯一可用的选项,在这里选择它可以使两个平台具有更好的一致性,并使两者之间的调试差异(如果您选择构建两者)更简单。
  2. 构建您的应用程序。在File > Build Settings中,完成以下操作:
    1. 确保选中“创建符号.zip” (或者如果出现下拉列表,请选择“调试”)。
    2. 直接从 Unity 编辑器将 APK 构建到您刚刚创建的Builds/Android子文件夹中。
  3. 构建完成后,您需要生成与 Crashlytics 兼容的符号文件并将其上传到 Firebase 服务器。需要此符号信息才能在 Crashlytics 仪表板中查看本机库崩溃的符号化堆栈跟踪。

    通过运行以下Firebase CLI命令生成并上传此符号文件:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
    • FIREBASE_APP_ID :您的 Firebase Android 应用 ID(不是您的包名称)。在您之前下载的google-services.json文件中找到该值。这是mobilesdk_app_id值。
      示例 Firebase Android 应用程序 ID: 1:567383003300:android:17104a2ced0c9b9b
    • PATH/TO/SYMBOLS :构建完成后在Builds/Android目录中生成的压缩符号文件的路径(例如: Builds/Android/myapp-1.0-v100.symbols.zip )。

强制测试崩溃以完成设置

要完成 Crashlytics 的设置并在 Firebase 控制台的 Crashlytics 仪表板中查看初始数据,您需要强制测试崩溃。

  1. MainGameScene 的编辑器Hierarchy中找到EmptyObject GameObject ,向其中添加以下脚本,然后保存场景。该脚本将在您运行应用程序几秒钟后导致测试崩溃。
    using System;
    using UnityEngine;
    
    public class CrashlyticsTester : MonoBehaviour {
        // Update is called once per frame
        void Update()
        {
            // Tests your Crashlytics implementation by
            // throwing an exception every 60 frames.
            // You should see reports in the Firebase console
            // a few minutes after running your app with this method.
            if(Time.frameCount >0 && (Time.frameCount%60) == 0)
            {
                throw new System.Exception("Test exception; please ignore");
            }
        }
    }
    
  2. 构建您的应用程序并在构建完成后上传符号信息。
    • iOS :Firebase Unity 编辑器插件会自动配置您的 Xcode 项目以上传符号文件。
    • Android :运行 Firebase CLI crashlytics:symbols:upload命令来上传符号文件。
  3. 运行您的应用程序。应用程序运行后,观察设备日志并等待CrashlyticsTester触发异常。
    • iOS :在 Xcode 底部窗格中查看日志。
    • Android :通过在终端中运行以下命令来查看日志: adb logcat
  4. 访问Crashlytics 仪表板查看异常!您将在仪表板底部的问题表中看到它。稍后在 Codelab 中,您将详细了解如何探索这些报告。
  5. 确认事件已上传到 Crashlytics 后,选择您附加到的EmptyObject GameObject ,仅删除CrashlyticsTester组件,然后保存场景以将其恢复到原始状态。

5. 启用并了解调试菜单

到目前为止,您已将 Crashlytics 添加到 Unity 项目、完成设置并确认 Crashlytics SDK 正在将事件上传到 Firebase。现在,您将在 Unity 项目中创建一个菜单,该菜单将演示如何在游戏中使用更高级的 Crashlytics 功能。 Level Up with Firebase Unity 项目已经有一个隐藏的调试菜单,您将使其可见并为其编写功能。

启用调试菜单

您的 Unity 项目中存在用于访问调试菜单的按钮,但当前未启用。您必须启用该按钮才能从MainMenu预制件中访问它:

  1. 在 Unity 编辑器中,打开名为MainMenu的预制件。 4148538cbe9f36c5.png
  2. 在预制件层次结构中,找到名为DebugMenuButton禁用子对象,然后选择它。 816f8f9366280f6c.png
  3. 通过选中包含DebugMenuButton文本字段左侧左上角的框来启用DebugMenuButton8a8089d2b4886da2.png
  4. 保存预制件。
  5. 在编辑器或您的设备上运行游戏。现在应该可以访问菜单了。

预览并了解调试菜单的方法体

稍后在此 Codelab 中,您将为一些预配置的调试 Crashlytics 方法编写方法体。不过,在Level Up with Firebase Unity 项目中,这些方法是在DebugMenu.cs中定义并调用的。

虽然其中一些方法将调用 Crashlytics 方法并引发错误,但 Crashlytics 捕获这些错误的能力并不依赖于首先调用这些方法。相反,自动捕获错误生成的崩溃报告将通过这些方法添加的信息得到增强。

打开DebugMenu.cs ,然后找到以下方法:

生成和注释 Crashlytics 问题的方法:

  • CrashNow
  • LogNonfatalError
  • LogStringsAndCrashNow
  • SetAndOverwriteCustomKeyThenCrash
  • SetLogsAndKeysBeforeANR

用于记录 Analytics 事件以帮助调试的方法:

  • LogProgressEventWithStringLiterals
  • LogIntScoreWithBuiltInEventAndParams

在此 Codelab 的后续步骤中,您将实现这些方法并了解它们如何帮助解决游戏开发中可能出现的特定情况。

6. 确保开发过程中崩溃报告的交付

在开始实现这些调试方法并查看它们如何影响崩溃报告之前,请确保您了解如何将事件报告给 Crashlytics。

对于 Unity 项目,游戏中的崩溃和异常事件会立即写入磁盘。对于不会导致游戏崩溃的未捕获异常(例如,游戏逻辑中未捕获的 C# 异常),您可以在 Unity 项目中初始化 Crashlytics时将Crashlytics.ReportUncaughtExceptionsAsFatal属性设置为true ,让 Crashlytics SDK 将它们报告为致命事件。这些事件会实时报告给 Crashlytics,而无需最终用户重新启动游戏。请注意,本机崩溃始终会报告为致命事件,并在最终用户重新启动游戏时发送。

此外,请注意不同运行时环境向 Firebase 发送 Crashlytics 信息的方式之间存在以下微小但重要的差异:

iOS模拟器:

  • 当且仅当您从模拟器中分离 Xcode 时,才会报告 Crashlytics 信息。如果附加了 Xcode,它会捕获上游的错误,从而阻止信息传递。

移动物理设备(Android 和 iOS):

  • Android 特定:仅在 Android 11+ 上报告 ANR。 ANR 和非致命事件将在下次运行时报告。

统一编辑器:

测试在CrashNow()中按一下按钮会导致游戏崩溃

在游戏中设置 Crashlytics 后,Crashlytics SDK 会自动记录崩溃和未捕获的异常,并将其上传到 Firebase 进行分析。报告显示在 Firebase 控制台的Crashlytics 仪表板中。

  1. 要证明这确实是自动的:打开DebugMenu.cs ,然后覆盖方法CrashNow()如下所示:
    void CrashNow()
    {
        TestCrash();
    }
    
  2. 构建您的应用程序。
  3. (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 点击“立即崩溃”按钮,然后继续执行此 Codelab 的下一步,了解如何查看和解释崩溃报告。

7. 了解 Firebase 控制台中的问题报告

在查看崩溃报告时,您需要了解更多有关如何充分利用它们的信息。您编写的每种方法都将展示如何将不同类型的信息添加到 Crashlytics 报告中。

  1. 点击“立即崩溃”按钮,然后重新启动您的应用程序。
  2. 转到Crashlytics 仪表板。向下滚动到仪表板底部的“问题”表,其中 Crashlytics 将所有具有相同根本原因的事件分组为“问题”。
  3. 单击问题表中列出的新问题。执行此操作会显示发送到 Firebase 的每个单独事件的事件摘要

    您应该看到类似以下屏幕截图的内容。请注意事件摘要如何突出显示导致崩溃的调用的堆栈跟踪。 40c96abe7f90c3aa.png

附加元数据

另一个有用的选项卡是“Unity 元数据”选项卡。本部分向您介绍发生事件的设备的属性,包括物理功能、CPU 型号/规格以及各种 GPU 指标。

以下是此选项卡中的信息可能有用的示例:
想象一下,您的游戏大量使用着色器来实现某种外观,但并非所有手机都具有能够渲染此功能的 GPU。 Unity 元数据选项卡中的信息可以让您更好地了解应用程序在决定自动提供或完全禁用哪些功能时应测试哪些硬件。

虽然您的设备上可能永远不会发生错误或崩溃,但由于 Android 设备的多样性,它有助于更​​好地了解受众设备的特定“热点”。

41d8d7feaa87454d.png

8. 抛出、捕获和记录异常

通常,作为开发人员,即使您的代码正确捕获并处理运行时异常,也最好注意它发生的情况以及在什么情况下发生。 Crashlytics.LogException可用于此确切目的 - 将异常事件发送到 Firebase,以便您可以在 Firebase 控制台中进一步调试问题。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs中,将以下内容附加到using语句中:
    // Import Firebase
    using Firebase.Crashlytics;
    
  2. 仍然在DebugMenu.cs中,覆盖LogNonfatalError()如下所示:
    void LogNonfatalError()
    {
        try
        {
            throw new System.Exception($"Test exception thrown in {nameof(LogNonfatalError)}");
        }
        catch(System.Exception exception)
        {
            Crashlytics.LogException(exception);
        }
    }
    
  3. 构建您的应用程序。
  4. (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  5. 点击记录非致命错误按钮,然后重新启动您的应用程序。
  6. 转到Crashlytics 仪表板,您应该会看到与此 Codelab 最后一步中看到的内容类似的内容。
  7. 不过,这一次,将事件类型过滤器限制为非致命错误,以便您只能查看非致命错误,例如您刚刚记录的错误。
    a39ea8d9944cbbd9.png

9. 将字符串记录到 Crashlytics 以更好地了解程序执行流程

您是否曾经尝试过弄清楚为什么从多个路径调用的一行代码(每个会话调用数百次甚至数千次)会突然生成异常或崩溃?虽然在 IDE 中单步执行代码并更仔细地查看这些值可能会很好,但如果这种情况只发生在极少数用户中怎么办?更糟糕的是,如果无论你做什么都无法复制这次崩溃,你会怎么做?

在这种情况下,了解一些背景信息可以使世界变得不同。使用Crashlytics.Log ,您可以写出所需的上下文。将这些消息视为对未来的自己可能发生的事情的暗示。

虽然日志有多种使用方式,但它们通常最适合记录呼叫顺序和/或缺席是至关重要的信息的情况。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs中,覆盖LogStringsAndCrashNow()如下:
    void LogStringsAndCrashNow()
    {
        Crashlytics.Log($"This is the first of two descriptive strings in {nameof(LogStringsAndCrashNow)}");
        const bool RUN_OPTIONAL_PATH = false;
        if(RUN_OPTIONAL_PATH)
        {
            Crashlytics.Log(" As it stands, this log should not appear in your records because it will never be called.");
        }
        else
        {
            Crashlytics.Log(" A log that will simply inform you which path of logic was taken. Akin to print debugging.");
        }
        Crashlytics.Log($"This is the second of two descriptive strings in {nameof(LogStringsAndCrashNow)}");
        TestCrash();
    }
    
  2. 构建您的应用程序。
  3. (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 点击“记录字符串并立即崩溃”按钮,然后重新启动您的应用程序。
  5. 返回Crashlytics 仪表板,然后单击问题表中列出的最新问题。您应该再次看到与前几期类似的内容。
    7aabe103b8589cc7.png
  6. 但是,如果单击“事件摘要”中的“日志”选项卡,您将看到如下视图:
    4e27aa407b7571cf.png

10. 写入并覆盖自定义密钥

假设您想更好地理解与设置为少量值或配置的变量相对应的崩溃。能够在任何给定时间根据您正在查看的变量和可能值的组合进行过滤可能会很好。

除了记录任意字符串之外,Crashlytics 还提供了另一种形式的调试,有助于了解程序崩溃时的确切状态:自定义键。

这些是您可以为会话设置的键值对。与累积且纯粹累加的日志不同,键可以被覆盖以仅反映变量或条件的最新状态。

除了作为程序最后记录状态的分类帐外,这些键还可以用作 Crashlytics 问题的强大过滤器。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs中,覆盖SetAndOverwriteCustomKeyThenCrash() ,如下所示:
    void SetAndOverwriteCustomKeyThenCrash()
    {
        const string CURRENT_TIME_KEY = "Current Time";
        System.TimeSpan currentTime = System.DateTime.Now.TimeOfDay;
        Crashlytics.SetCustomKey(
            CURRENT_TIME_KEY,
            DayDivision.GetPartOfDay(currentTime).ToString() // Values must be strings
            );
    
        // Time Passes
        currentTime += DayDivision.DURATION_THAT_ENSURES_PHASE_CHANGE;
    
        Crashlytics.SetCustomKey(
            CURRENT_TIME_KEY,
            DayDivision.GetPartOfDay(currentTime).ToString()
            );
        TestCrash();
    }
    
  2. 构建您的应用程序。
  3. (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 点击“设置自定义密钥和崩溃”按钮,然后重新启动您的应用程序。
  5. 返回Crashlytics 仪表板,然后单击问题表中列出的最新问题。您应该再次看到与前几期类似的内容。
  6. 不过,这一次,单击“事件摘要”中的“键”选项卡,以便您可以查看包括Current Time在内的键的值:
    7dbe1eb00566af98.png

为什么要使用自定义键而不是自定义日志?

  • 日志擅长存储顺序数据,但如果您只需要最新值,则自定义键会更好。
  • 在 Firebase 控制台中,您可以通过问题表搜索框中的键值轻松过滤问题。

但与日志类似,自定义键也有限制。 Crashlytics 最多支持 64 个键值对。达到此阈值后,不会保存其他值。每个键值对的大小最大可达 1 KB。

11.(仅限 Android)使用自定义键和日志来了解和诊断 ANR

对于 Android 开发人员来说,最难调试的一类问题是应用程序无响应(ANR) 错误。当应用程序无法响应输入超过 5 秒时,就会发生 ANR。如果发生这种情况,则意味着应用程序要么冻结,要么运行速度非常慢。将向用户显示一个对话框,他们可以选择是“等待”还是“关闭应用程序”。

ANR 是一种糟糕的用户体验,并且(如上面的 ANR 链接中所述)可能会影响您的应用在 Google Play 商店中的可发现性。由于它们的复杂性,并且它们通常是由在不同手机型号上行为截然不同的多线程代码引起的,因此在调试时重现 ANR 通常非常困难,甚至几乎不可能。因此,通过分析和演绎的方式来处理它们通常是最好的方法。

在此方法中,我们将使用Crashlytics.LogExceptionCrashlytics.LogCrashlytics.SetCustomKey的组合来补充自动问题日志记录并为我们提供更多信息。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs中,覆盖SetLogsAndKeysBeforeANR() ,如下所示:
    void SetLogsAndKeysBeforeANR()
    {
        System.Action<string,long> WaitAndRecord =
        (string methodName, long targetCallLength)=>
        {
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            const string CURRENT_FUNCTION = "Current Async Function";
    
            // Initialize key and start timing
            Crashlytics.SetCustomKey(CURRENT_FUNCTION, methodName);
            stopWatch.Start();
    
            // The actual (simulated) work being timed.
            BusyWaitSimulator.WaitOnSimulatedBlockingWork(targetCallLength);
    
            // Stop timing
            stopWatch.Stop();
    
            if(stopWatch.ElapsedMilliseconds>=BusyWaitSimulator.EXTREME_DURATION_MILLIS)
            {
              Crashlytics.Log($"'{methodName}' is long enough to cause an ANR.");
            }
            else if(stopWatch.ElapsedMilliseconds>=BusyWaitSimulator.SEVERE_DURATION_MILLIS)
            {
              Crashlytics.Log($"'{methodName}' is long enough it may cause an ANR");
            }
        };
    
        WaitAndRecord("DoSafeWork",1000L);
        WaitAndRecord("DoSevereWork",BusyWaitSimulator.SEVERE_DURATION_MILLIS);
        WaitAndRecord("DoExtremeWork",2*BusyWaitSimulator.EXTREME_DURATION_MILLIS);
    }
    
  2. 构建您的应用程序。
  3. 通过运行以下 Firebase CLI 命令上传符号:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 点击标记为Set Logs And Keys → ANR 的按钮,然后重新启动您的应用程序。
  5. 返回到Crashlytics 仪表板,然后单击问题表中的新问题以查看事件摘要。如果调用正常进行,您应该看到如下内容:
    876c3cff7037bd07.png

    如您所见,Firebase 指出线程上的繁忙等待是您的应用触发 ANR 的主要原因。
  6. 如果您查看“事件摘要”“日志”选项卡中的日志,您将看到最后记录为完成的方法是DoSevereWork
    5a4bec1cf06f6984.png

    相比之下,最后列出的启动方法是DoExtremeWork ,这表明在此方法期间发生了 ANR,并且游戏在记录DoExtremeWork之前就关闭了。

    89d86d5f598ecf3a.png

为什么要这样做?

  • 重现 ANR 非常困难,因此能够获得有关代码区域和指标的丰富信息对于演绎地找出它非常重要。
  • 通过存储在自定义键中的信息,您现在可以知道哪个异步线程运行时间最长,以及哪些线程有触发 ANR 的危险。这种相关的逻辑和数字数据将向您显示代码中最需要优化的位置。

12. 穿插Analytics事件,进一步丰富报告

也可以从“调试”菜单调用以下方法,但它们本身不会生成问题,而是使用 Google Analytics 作为另一个信息源,以更好地了解游戏的工作原理。

与您在此 Codelab 中编写的其他方法不同,您应该将这些方法与其他方法结合使用。在运行其他方法之一之前,以您想要的任意顺序调用这些方法(通过按调试菜单中相应的按钮)。然后,当您检查特定 Crashlytics 问题中的信息时,您将看到 Analytics 事件的有序日志。这些数据可用于游戏中,以更好地理解程序流或用户输入的组合,具体取决于您如何检测应用程序。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs中,覆盖以下方法的现有实现:
    public void LogProgressEventWithStringLiterals()
    {
          Firebase.Analytics.FirebaseAnalytics.LogEvent("progress", "percent", 0.4f);
    }
    
    public void LogIntScoreWithBuiltInEventAndParams()
    {
          Firebase.Analytics.FirebaseAnalytics
            .LogEvent(
              Firebase.Analytics.FirebaseAnalytics.EventPostScore,
              Firebase.Analytics.FirebaseAnalytics.ParameterScore,
              42
            );
    }
    
  2. 构建并部署您的游戏,然后进入“调试菜单”
  3. (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 至少按一次或多次以下按钮之一可调用上述功能:
    • 记录字符串事件
    • 记录事件
  5. “立即崩溃”按钮。
  6. 重新启动游戏以将崩溃事件上传到 Firebase。
  7. 当您记录各种任意的 Analytics 事件序列,然后让您的游戏生成一个事件,Crashlytics 从中创建一个报告(正如您刚刚所做的那样)时,它们会添加到 Crashlytics事件摘要“日志”选项卡中,如下所示:
    d3b16d78f76bfb04.png

13. 展望未来

这样,您应该有更好的理论基础来补充自动生成的崩溃报告。这些新信息允许您使用当前状态、过去事件的记录和现有的 Google Analytics 事件来更好地分解事件的顺序和导致其结果的逻辑。

如果您的应用面向 Android 11(API 级别 30)或更高版本,请考虑合并GWP-ASan ,这是一种本机内存分配器功能,可用于调试由本机内存错误(例如use-after-freeheap-buffer-overflow错误)引起的崩溃。要利用此调试功能,请显式启用 GWP-ASan

下一步

继续使用远程配置测试您的 Unity 游戏Codelab,您将在其中了解如何在 Unity 中使用远程配置和 A/B 测试。