1.简介
在此 Codelab 中,您将学习如何使用 Crashlytics 的高级功能,帮助您更好地了解崩溃以及可能导致崩溃的环境因素。
您将为示例游戏 MechaHamster: Level Up with Firebase Edition 添加新功能。此示例游戏是经典 Firebase 游戏 MechaHamster 的新版本,该版本移除了其大部分内置 Firebase 功能,让您有机会取代 Firebase 的新用途。
您将向游戏添加一个调试菜单。此调试菜单会调用您将创建的方法,并可让您运用 Crashlytics 的各种功能。这些方法将向您展示如何使用自定义键、自定义日志、非严重错误等为自动崩溃报告添加注解。
构建游戏后,您将使用调试菜单并检查结果,以了解它们为游戏在野外运行提供的独特视图。
学习内容
- Crashlytics 自动捕获的错误类型。
- 可以刻意记录的其他错误。
- 如何为这些错误添加更多信息,使其更易于理解。
您需要满足的条件
- Unity(建议的最低版本 2019+)满足以下一项或两项要求:
- iOS build 支持
- Android build 支持
- (仅限 Android)Firebase CLI(用于上传崩溃报告符号)
- 按照说明安装 Firebase CLI。
如果您已安装 CLI,请务必更新到其最新版本。
- 按照说明安装 Firebase CLI。
2. 设置您的开发环境
以下部分介绍了如何下载 Level Up with Firebase 代码并在 Unity 中打开该代码。
请注意,其他几个 Firebase + Unity Codelab 也会使用此通过 Firebase 升级示例游戏,因此您可能已完成本部分中的任务。如果是这样,您可以直接进入本页面的最后一步:“添加 Firebase SDK for Unity”。
下载代码
从命令行克隆此 Codelab 的 GitHub 代码库:
git clone https://github.com/firebase/level-up-with-firebase.git
或者,如果您尚未安装 git,可以下载 ZIP 文件形式的代码库。
在 Unity 编辑器中打开 Level Up with Firebase
- 启动 Unity Hub,然后在 Projects 标签页中点击 Open 旁边的下拉箭头。
- 点击从磁盘添加项目。
- 转到包含代码的目录,然后点击 OK。
- 如果出现提示,请选择要使用的 Unity 编辑器版本和您的目标平台(Android 或 iOS)。
- 点击项目名称 level-up-with-firebase,项目将在 Unity 编辑器中打开。
- 如果您的编辑器未自动打开它,请在 Assets 中打开
MainGameScene
>Unity 编辑器的 Project 标签页中的 Hamster。
如需详细了解如何安装和使用 Unity,请参阅在 Unity 中工作。
3. 将 Firebase 添加到您的 Unity 项目
创建 Firebase 项目
- 在 Firebase 控制台中,点击添加项目。
- 如需创建新项目,请输入要使用的项目名称。
此操作还会根据项目名称将项目 ID(显示在项目名称下方)设置为一个特定的值。您可以选择性地点击项目 ID 上的修改图标,进一步对其进行自定义。 - 如果看到相关提示,请查看并接受 Firebase 条款。
- 点击继续。
- 选择为此项目启用 Google Analytics 选项,然后点击继续。
- 选择要使用的现有 Google Analytics 账号,或选择创建新账号来创建新账号。
- 点击 Create project。
- 创建项目后,点击继续。
在 Firebase 中注册您的应用
- 还是在 Firebase 控制台中,在项目概览页面的中心位置,点击 Unity 图标以启动设置工作流;如果您已向 Firebase 项目添加了应用,请点击添加应用以显示平台选项。
- 选择同时注册 Apple (iOS) 和 Android build 目标。
- 输入 Unity 项目的平台专用 ID。对于此 Codelab,请输入以下内容:
- 对于 Apple (iOS):在 iOS 软件包 ID 字段中输入
com.google.firebase.level-up
。 - 对于 Android:在 Android 软件包名称字段中输入
com.google.firebase.level_up
。
- 对于 Apple (iOS):在 iOS 软件包 ID 字段中输入
- (可选)输入 Unity 项目针对具体平台的别名。
- 点击注册应用,然后继续下载配置文件部分。
添加 Firebase 配置文件
点击 Register app(注册应用)后,系统会提示您下载两个配置文件(每个构建目标对应一个配置文件)。您的 Unity 项目需要这些文件中的 Firebase 元数据才能与 Firebase 关联。
- 下载两个可用的配置文件:
- 对于 Apple (iOS):下载 GoogleService-Info.plist。
- 对于 Android 应用:下载 google-services.json。
- 打开 Unity 项目的 Project 窗口,然后将两个配置文件都移到 Assets 文件夹中。<ph type="x-smartling-placeholder">
- 返回 Firebase 控制台,在设置工作流中,点击下一步,然后继续“添加 Firebase SDK for Unity”。
添加适用于 Unity 的 Firebase SDK
- 在 Firebase 控制台中点击下载 Firebase Unity SDK。
- 将 SDK 解压缩到方便的位置。
- 在您打开的 Unity 项目中,前往 Assets >导入软件包 >自定义软件包。
- 在 Import package 对话框中,转到包含解压缩的 SDK 的目录,选择
FirebaseAnalytics.unitypackage
,然后点击 Open。 - 在随即显示的 Import Unity Package 对话框中,点击 Import。
- 重复前面的步骤导入
FirebaseCrashlytics.unitypackage
。 - 返回 Firebase 控制台,然后在设置工作流中点击下一步。
如需详细了解如何将 Firebase SDK 添加到 Unity 项目,请参阅其他 Unity 安装选项。
4. 在您的 Unity 项目中设置 Crashlytics
要在 Unity 项目中使用 Crashlytics,您需要再执行几个设置步骤。当然,您需要初始化 SDK。此外,您还需要上传您的符号,这样才能在 Firebase 控制台中看到经过符号化解析的堆栈轨迹,并且您需要强制造成一次测试崩溃,以确保 Firebase 会收到您的崩溃事件。
初始化 Crashlytics SDK
- 在
Assets/Hamster/Scripts/MainGame.cs
中,添加以下using
语句:
第一个模块允许您使用 Crashlytics SDK 中的方法,第二个模块包含 C# Tasks API 的一些扩展。如果两个using Firebase.Crashlytics; using Firebase.Extensions;
using
语句都没有,以下代码将无法正常运行。 - 还是在
MainGame.cs
中,通过调用InitializeFirebaseAndStartGame()
将 Firebase 初始化添加到现有的void Start() { Screen.SetResolution(Screen.width / 2, Screen.height / 2, true); InitializeFirebaseAndStartGame(); }
Start()
方法中: - 同样,在
MainGame.cs
中找到InitializeFirebaseAndStartGame()
,声明一个应用变量,然后覆盖该方法的实现,如下所示: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+(Apple 平台)
- 在“Build Settings”(构建设置)对话框中,将您的项目导出到 Xcode 工作区。
- 构建您的应用。
对于 Apple 平台,Firebase Unity Editor 插件会自动配置您的 Xcode 项目,以便为每个 build 生成与 Crashlytics 兼容的符号文件并将其上传到 Firebase 服务器。必须提供这些符号信息才能在 Crashlytics 信息中心内查看经过符号化解析的堆栈轨迹。
Android
- (仅在初始设置期间,并非针对每个 build)设置 build:
- 在项目的根目录(即与 Assets 目录的同级)中创建一个名为 Builds 的新文件夹,然后创建一个名为 Android 的子文件夹。
- 在文件 > 中构建设置 >播放器设置 >Configuration 中,将 Scripting Backend 设为 IL2CPP。
- IL2CPP 通常会导致 build 更小,性能更好。
- IL2CPP 也是 iOS 上唯一的选项,在此处选择它可使两个平台更好地对等,并简化两个平台之间的调试差异(如果您选择同时构建这两种平台)。
- 构建您的应用。在文件 > 中Build Settings 中),完成以下操作:
- 确保已选中 Create strings.zip(如果系统显示下拉菜单,则选择调试)。
- 直接从 Unity 编辑器将 APK 构建到您刚刚创建的 Builds/Android 子文件夹中。
- 构建完成后,您需要生成与 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 信息中心内查看初始数据,您需要强制造成一次测试崩溃。
- 在 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"); } } }
- 构建您的应用,并在构建完成后上传符号信息。
- iOS:Firebase Unity Editor 插件会自动配置您的 Xcode 项目,以上传您的符号文件。
- Android:运行 Firebase CLI
crashlytics:symbols:upload
命令上传您的符号文件。
- 运行应用。应用运行后,观察设备日志并等待
CrashlyticsTester
触发异常。- iOS:在 Xcode 的底部窗格中查看日志。
- Android:在终端中运行以下命令来查看日志:
adb logcat
。
- 请访问 Crashlytics 信息中心查看异常!您会在信息中心底部的问题表格中看到该问题。稍后在此 Codelab 中,您将详细了解如何探索这些报告。
- 确认事件已上传到 Crashlytics 后,选择您附加到的 EmptyObject
GameObject
,仅移除CrashlyticsTester
组件,然后保存场景以将其恢复为原始状态。
5. 启用并了解调试菜单
到目前为止,您已将 Crashlytics 添加到您的 Unity 项目中,完成了设置,并且已确认 Crashlytics SDK 正在将事件上传到 Firebase。现在,您将在 Unity 项目中创建一个菜单,以演示如何在游戏中使用更高级的 Crashlytics 功能。Level Up with Firebase Unity 项目已有一个隐藏的调试菜单,您将能看到该菜单并为该菜单编写相应的功能。
启用调试菜单
Unity 项目中已存在用于访问调试菜单的按钮,但目前未启用。您必须启用该按钮,才能从 MainMenu
预设件中访问该按钮:
- 在 Unity 编辑器中,打开名为
MainMenu
的预设件。 - 在 Prefab 层次结构中,找到名为“
DebugMenuButton
”的已停用子对象,然后将其选中。 - 勾选包含
DebugMenuButton
的文本字段左上角的复选框,启用DebugMenuButton
。 - 保存预设件。
- 在编辑器或设备上运行游戏。该菜单现在应该可以访问了。
预览并了解“调试”菜单的方法正文
在此 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# 异常),您可以将 Crashlytics.ReportUncaughtExceptionsAsFatal
属性设置为 true
(在此在您的 Unity 项目中初始化 Crashlytics),让 Crashlytics SDK 将这些异常报告为严重事件。这些事件会实时报告给 Crashlytics,而无需最终用户重启游戏。请注意,原生代码崩溃始终会报告为严重事件,并在最终用户重启游戏时一并发送。
此外,请注意不同运行时环境向 Firebase 发送 Crashlytics 信息的方式之间存在以下微小但重大的差异:
iOS 模拟器:
- 当且仅当您将 Xcode 从模拟器中分离时,系统才会报告 Crashlytics 信息。如果附加了 Xcode,它会捕获上游错误,阻止信息传送。
移动实体设备(Android 和 iOS):
- Android 专用:ANR 仅在 Android 11 及更高版本上报告。系统会在下一次运行时报告 ANR 和非严重事件。
Unity 编辑器:
- 在播放模式或独立模式下来自编辑器的 Crashlytics 信息不会被记录或上传到 Firebase。此外,Firebase 桌面开发工作流不支持 Crashlytics。
测试只要轻触 CrashNow()
中的按钮,游戏就会崩溃
在游戏中设置 Crashlytics 后,Crashlytics SDK 会自动记录崩溃和未捕获的异常,并将它们上传到 Firebase 进行分析。报告显示在 Firebase 控制台的 Crashlytics 信息中心内。
- 为了证明这确实是自动进行的,请打开
DebugMenu.cs
,然后覆盖CrashNow()
方法,如下所示:void CrashNow() { TestCrash(); }
- 构建您的应用。
- (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 点按 Crash Now 按钮,然后继续执行此 Codelab 的下一步,了解如何查看和解读崩溃报告。
7. 了解 Firebase 控制台中的问题报告
在查看崩溃报告时,您需要知道如何充分利用此类报告。您编写的每种方法都将演示如何向 Crashlytics 报告添加不同类型的信息。
- 点按 Crash Now 按钮,然后重启您的应用。
- 前往 Crashlytics 信息中心。向下滚动到信息中心底部的 Issues(问题)表格,Crashlytics 在这里将根本原因相同的事件划分为“问题”。
- 点击问题表格中列出的新问题。执行此操作后,系统会显示发送到 Firebase 的每个事件的事件摘要。
您应该会看到如下屏幕截图。请注意事件摘要如何突出显示导致崩溃的调用的堆栈轨迹。
其他元数据
另一个有用的标签页是 Unity Metadata 标签页。此部分会告知您发生事件的设备的属性,包括物理特征、CPU 型号/规格和各种 GPU 指标。
在这个例子中,这个标签页中的信息可能很有用:
假设您的游戏大量使用着色器来实现某种外观,但并非所有手机都配备能够呈现此功能的 GPU。通过 Unity Metadata 标签页中的信息,您可以更好地了解在决定自动提供或完全停用哪些功能时,您的应用应测试哪些硬件。
虽然您的设备上永远不会出现 bug 或崩溃,但由于野外的 Android 设备种类繁多,因此这有助于更好地了解特定“热点”问题受众群体的设备。
8. 抛出、捕获和记录异常
作为开发者,很多时候,即使代码正确捕获和处理运行时异常,也应该注意它的发生以及在什么情况下发生。Crashlytics.LogException
正用于此确切目的,即向 Firebase 发送异常事件,以便您可以在 Firebase 控制台中进一步调试问题。
- 在
Assets/Hamster/Scripts/States/DebugMenu.cs
中,将以下内容附加到using
语句:// Import Firebase using Firebase.Crashlytics;
- 仍在
DebugMenu.cs
中,按如下方式覆盖LogNonfatalError()
:void LogNonfatalError() { try { throw new System.Exception($"Test exception thrown in {nameof(LogNonfatalError)}"); } catch(System.Exception exception) { Crashlytics.LogException(exception); } }
- 构建您的应用。
- (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 点按记录非严重错误按钮,然后重启应用。
- 转到 Crashlytics 信息中心,您应该会看到与此 Codelab 的最后一步类似的内容。
- 不过,这一次,请将事件类型过滤条件限制为非严重,以便仅显示非严重错误,例如您刚刚记录的错误。
9. 将字符串记录到 Crashlytics 中,以便更好地了解程序的执行流程
您是否曾尝试弄清楚为什么从多个路径调用一行代码(每次会话数百次甚至数千次)会突然生成异常或崩溃?虽然在 IDE 中单步调试代码并仔细查看值可能不错,但如果这种情况只发生在一小部分用户身上,该怎么办?更糟糕的是,如果您无论执行什么操作都无法重现此崩溃问题,您会怎么做?
在这种情况下,掌握一些背景信息可能会带来很大的不同。借助 Crashlytics.Log
,您可以写出所需的上下文。您可以将这些消息看作是对未来可能发生的事情的提示。
虽然日志的用途多种多样,但对于记录调用顺序和/或缺失调用是非常重要的信息的情况,它们通常最为有用。
- 在
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(); }
- 构建您的应用。
- (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 点按 Log Strings and Crash Now 按钮,然后重启应用。
- 返回 Crashlytics 信息中心,然后点击问题表格中列出的最新问题。您应该会看到与上述问题类似的一些信息。
- 不过,如果您点击事件摘要中的日志标签页,则会看到如下所示的视图:
10. 写入和覆盖自定义键
假设您想要更好地了解与设置为少量值或配置的变量对应的崩溃。如果能够在任何给定时间根据变量和可能值的组合进行过滤,可能会很不错。
除了记录任意字符串之外,Crashlytics 还提供了另一种形式的调试,即自定义键,它可以帮助您了解程序在崩溃时的确切状态。
这些是您可以为会话设置的键值对。与会累积且完全累加的日志不同,键可以被覆盖,仅反映变量或条件的最新状态。
除了用作程序上次记录状态的分类记录器之外,这些键还可以用作 Crashlytics 问题的强大过滤器。
- 在
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(); }
- 构建您的应用。
- (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 点按 Set Custom Key and Crash 按钮,然后重启您的应用。
- 返回 Crashlytics 信息中心,然后点击问题表格中列出的最新问题。同样,您应该会看到与之前问题类似的一些内容。
- 不过,这一次请点击事件摘要中的键标签页,以便查看包含
Current Time
的键的值:
为什么要使用自定义键而不是自定义日志?
- 日志非常适合存储顺序数据,但如果您只想使用最新值,则自定义键会更好。
- 在 Firebase 控制台中,您可以在问题表格搜索框中按键的值轻松过滤问题。
但与日志类似,自定义键也存在限制。Crashlytics 最多支持 64 个键值对。达到此阈值后,系统就不会再保存更多的值。每个键值对的大小上限为 1 KB。
11. (仅限 Android)使用自定义键和日志来了解和诊断 ANR
对于 Android 开发者来说,要调试的一类最困难的问题是“应用无响应”(ANR) 错误。当应用超过 5 秒未能响应输入时,就会发生 ANR。如果发生这种情况,则表示应用卡顿或运行速度非常缓慢。系统会向用户显示一个对话框,让他们可以选择是否“等待”或“关闭应用”
ANR 会给用户带来糟糕的体验(如上面的 ANR 链接中所述)可能会影响您的应用在 Google Play 商店中的曝光度。由于 ANR 的复杂性,并且这些错误往往是由具有不同手机型号行为的多线程代码引起的,因此在调试时重现 ANR 往往非常困难,即使并非几乎不可能。因此,最好从分析和演绎角度进行分析。
在此方法中,我们将结合使用 Crashlytics.LogException
、Crashlytics.Log
和 Crashlytics.SetCustomKey
来补充自动记录问题并向我们提供更多信息。
- 在
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); }
- 构建您的应用。
- 运行以下 Firebase CLI 命令,上传符号:
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 点按设置日志和密钥 → ANR 按钮,然后重启应用。
- 返回 Crashlytics 信息中心,然后在问题表格中点击进入新问题,以查看事件摘要。如果通话正常,您应该会看到如下内容:
如您所见,Firebase 确定线程中的繁忙等待是应用触发 ANR 的主要原因。 - 在事件摘要的日志标签页中查看日志时,您会看到最后记录为完成的方法为
DoSevereWork
。
相比之下,列为起始方法的最后一个方法是DoExtremeWork
,表示该方法期间发生了 ANR,游戏在记录DoExtremeWork
之前就已关闭。
为什么要这样做?
- 重现 ANR 极其困难,因此能够获得关于代码区域和指标的丰富信息对于以推理方式找到问题极为重要。
- 通过存储在自定义键中的信息,您现在可以知道哪个异步线程的运行时间最长,以及哪些异步线程有触发 ANR 的风险。这些相关的逻辑数据和数字数据会向您显示代码中最有必要优化的环节。
12. 穿插 Analytics 事件以进一步丰富报告
您也可从“调试”菜单中调用以下方法,但这些方法不会自行生成问题,而是使用 Google Analytics 作为另一种信息来源,以便更好地了解游戏的运行情况。
与您在此 Codelab 中编写的其他方法不同,您应该将这些方法与其他方法结合使用。在运行其他某个方法之前,按任意顺序调用这些方法(通过按调试菜单中相应的按钮)。然后,在检查特定 Crashlytics 问题中的信息时,您会看到 Analytics 事件的有序日志。这些数据可用于在游戏中更好地了解程序流程或用户输入的组合,具体取决于您如何检测应用。
- 在
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 ); }
- 构建和部署游戏,然后输入调试菜单。
- (仅限 Android)通过运行以下 Firebase CLI 命令上传符号:
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 至少按下列其中一个按钮一次或多次以调用上述函数:
- 日志字符串事件
- Log Int 事件
- 按下 Crash Now 按钮。
- 重启游戏,让游戏将崩溃事件上传到 Firebase。
- 当您记录各种任意序列的 Analytics 事件,然后让您的游戏生成由 Crashlytics 创建报告的事件(和您刚才一样)时,这些事件会添加到 Crashlytics 事件摘要的日志标签页,如下所示:
13. 今后
因此,对于自动生成的崩溃报告,您应该有更好的理论依据。通过这些新信息,您可以利用当前状态、过往事件的记录以及现有的 Google Analytics 事件,更好地细分事件和逻辑,从而实现事件结果的排序。
如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,请考虑结合使用 GWP-ASan,这是一项原生内存分配器功能,有助于调试由原生内存错误(例如 use-after-free
和 heap-buffer-overflow
bug)导致的崩溃。如需利用此调试功能,请明确启用 GWP-ASan。
后续步骤
继续学习使用 Remote Config 对 Unity 游戏进行插桩测试 Codelab 中,您将了解如何在 Unity 中使用 Remote Config 和 A/B Testing。