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 中打开它。
请注意,这款 Level Up with Firebase 示例游戏还用于其他几个 Firebase + Unity Codelab,因此您可能已经完成了本部分中的任务。如果是,您可以直接前往本页面的最后一步:“添加 Firebase SDK for Unity”。
下载代码
从命令行克隆此 Codelab 的 GitHub 代码库:
git clone https://github.com/firebase/level-up-with-firebase.git
或者,如果您未安装 git,可以以 ZIP 文件的形式下载代码库。
在 Unity 编辑器中打开借助 Firebase 进行升级
- 启动 Unity Hub,然后在项目标签页中,点击打开旁边的下拉箭头。
- 点击从磁盘添加项目。
- 前往包含代码的目录,然后点击 OK。
- 如果系统提示,请选择要使用的 Unity 编辑器版本和目标平台(Android 或 iOS)。
- 点击项目名称 level-up-with-firebase,项目将在 Unity 编辑器中打开。
- 如果编辑器未自动打开
MainGameScene
,请在 Unity 编辑器的 Project(项目)标签页中依次打开 Assets(资源)> Hamster。
如需详细了解如何安装和使用 Unity,请参阅 Working in Unity。
3. 将 Firebase 添加到您的 Unity 项目
创建 Firebase 项目
- 使用您的 Google 账号登录 Firebase 控制台。
- 点击相应按钮以创建新项目,然后输入项目名称(例如
Mechahamster Codelab
)。
- 点击继续。
- 如果看到相关提示,请查看并接受 Firebase 条款,然后点击继续。
- (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
- 在此 Codelab 中,您需要使用 Google Analytics 才能充分利用 Firebase 产品,因此请确保 Google Analytics 选项的开关处于开启状态。按照屏幕上的说明设置 Google Analytics。
- 点击创建项目,等待项目完成预配,然后点击继续。
在 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 配置文件
点击注册应用后,系统会提示您下载两个配置文件(每个 build 目标对应一个配置文件)。您的 Unity 项目需要这些文件中的 Firebase 元数据才能与 Firebase 连接。
- 下载两个可用的配置文件:
- 对于 Apple (iOS):下载 GoogleService-Info.plist。
- 对于 Android:下载 google-services.json。
- 打开 Unity 项目的 Project 窗口,然后将这两个配置文件移动到 Assets 文件夹中。
- 返回 Firebase 控制台,在设置工作流中,点击下一步,然后继续操作以添加适用于 Unity 的 Firebase SDK。
添加适用于 Unity 的 Firebase SDK
- 在 Firebase 控制台中,点击下载 Firebase Unity SDK。
- 将 SDK 解压缩到方便的位置。
- 在您打开的 Unity 项目中,依次点击 Assets(资源)> Import Package(导入软件包)> Custom Package(自定义软件包)。
- 在导入软件包对话框中,前往包含解压缩的 SDK 的目录,选择
FirebaseAnalytics.unitypackage
,然后点击打开。 - 在随即显示的 Import Unity Package 对话框中,点击 Import。
- 重复上述步骤以导入
FirebaseCrashlytics.unitypackage
。 - 返回 Firebase 控制台,然后在设置工作流中点击下一步。
如需详细了解如何将 Firebase SDK 添加到 Unity 项目,请参阅其他 Unity 安装选项。
4. 在 Unity 项目中设置 Crashlytics
如需在 Unity 项目中使用 Crashlytics,您还需要完成一些设置步骤。当然,您需要初始化 SDK。不过,您还需要上传符号,以便在 Firebase 控制台中查看符号化的 stacktrace;并且您需要强行制造一次测试崩溃,以确保 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 初始化添加到现有Start()
方法:void Start() { Screen.SetResolution(Screen.width / 2, Screen.height / 2, true); InitializeFirebaseAndStartGame(); }
- 同样,在
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 的子文件夹。
- 在 File > Build Settings > Player Settings > Configuration 中,将 Scripting Backend 设置为 IL2CPP。
- IL2CPP 通常会使 build 更小,并具有更好的性能。
- IL2CPP 也是 iOS 上唯一可用的选项,在此处选择它可让这两个平台更好地保持一致,并简化这两个平台之间的调试差异(如果您选择同时构建这两个平台)。
- 构建应用。在文件 > 构建设置中,完成以下操作:
- 确保已选中 Create symbols.zip(如果显示下拉菜单,请选择 Debugging)。
- 直接通过 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 中,找到编辑器层次结构中的 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 功能。使用 Firebase 升级 Unity 项目已有一个隐藏的调试菜单,您将使其可见并为其编写功能。
启用调试菜单
您的 Unity 项目中存在用于访问调试菜单的按钮,但该按钮目前处于未启用状态。您必须启用该按钮才能从 MainMenu
预制件访问它:
- 在 Unity 编辑器中,打开名为
MainMenu
的预制件。 - 在预制件层次结构中,找到名为
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 SDK 将其报告为严重事件,具体方法是,在 Unity 项目中初始化 Crashlytics 的地方将 Crashlytics.ReportUncaughtExceptionsAsFatal
属性设置为 true
。这些事件会实时报告给 Crashlytics,而无需最终用户重启游戏。请注意,原生代码崩溃始终会报告为严重事件,并在最终用户重启游戏时一并发送。
此外,请注意以下细微但重要的差异:不同的运行时环境向 Firebase 发送 Crashlytics 信息的方式有所不同:
iOS 模拟器:
- 只有在您将 Xcode 与模拟器分离时,系统才会报告 Crashlytics 信息。如果 Xcode 已附加,它会在上游捕获错误,从而阻止信息传递。
移动实体设备(Android 和 iOS):
- Android 特有的:仅在 Android 11 及更高版本上报告 ANR。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 信息中心。向下滚动到信息中心底部的问题表格,其中 Crashlytics 会将具有相同根本原因的事件归为“问题”。
- 点击问题表格中列出的新问题。执行此操作会显示发送到 Firebase 的每个单独事件的事件摘要。
您应该会看到类似以下屏幕截图的内容。请注意,事件摘要会突出显示导致崩溃的调用的堆栈轨迹。
其他元数据
另一个有用的标签页是 Unity 元数据标签页。此部分会告知您发生事件的设备的属性,包括物理特征、CPU 型号/规格和各种 GPU 指标。
下面举例说明了此标签页中的信息可能很有用:
假设您的游戏大量使用着色器来实现某种外观,但并非所有手机都配备了能够渲染此功能的 GPU。Unity 元数据标签页中的信息可让您更好地了解应用在决定自动提供或完全停用哪些功能时应测试哪些硬件。
虽然 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>
- 点按 Log Nonfatal Error 按钮,然后重启应用。
- 前往 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>
- 点按标签为 Set Logs And Keys → ANR 的按钮,然后重启应用。
- 返回 Crashlytics 信息中心,然后点击问题表格中的新问题,查看事件摘要。如果调用正常完成,您应该会看到类似以下内容:
如您所见,Firebase 将线程上的繁忙等待确定为应用触发 ANR 的主要原因。 - 如果您查看事件摘要的日志标签页中的日志,会发现记录为已完成的最后一个方法是
DoSevereWork
.
。相比之下,列为已开始的最后一个方法是DoExtremeWork
,这表示 ANR 是在此方法期间发生的,并且游戏在记录DoExtremeWork
.之前就已关闭。
为什么要这样做?
- 重现 ANR 非常困难,因此能够获取有关代码区域和指标的丰富信息对于演绎推理出 ANR 非常重要。
- 借助存储在自定义键中的信息,您现在可以知道哪个异步线程的运行时间最长,以及哪些异步线程有触发 ANR 的风险。这类相关逻辑数据和数值数据将显示代码中哪些部分最需要优化。
12. 穿插 Analytics 事件以进一步丰富报告
以下方法也可从调试菜单中调用,但它们不会自行生成问题,而是将 Google Analytics 用作另一个信息来源,以便更好地了解游戏的运作方式。
与您在此 Codelab 中编写的其他方法不同,您应将这些方法与其他方法结合使用。在运行其他方法之前,您可以按任意顺序调用这些方法(通过在调试菜单中按其对应的按钮)。然后,当您检查特定 Crashlytics 问题中的信息时,您会看到按顺序排列的 Google 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>
- 按以下至少一个按钮一次或多次,即可调用上述功能:
- 记录字符串事件
- 记录 Int 事件
- 按立即崩溃按钮。
- 重新启动游戏,以便将崩溃事件上传到 Firebase。
- 当您记录各种任意序列的 Analytics 事件,然后让游戏生成一个 Crashlytics 会据此创建报告的事件(如您刚才所做的那样)时,这些事件会添加到 Crashlytics 事件摘要的日志标签页中,如下所示:
13. 展望未来
这样一来,您就有了更好的理论基础来补充自动生成的崩溃报告。借助这些新信息,您可以利用当前状态、过往事件记录和现有的 Google Analytics 事件,更好地分析导致相应结果的事件序列和逻辑。
如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,请考虑加入 GWP-ASan,这是一种原生内存分配器功能,可用于调试由 use-after-free
和 heap-buffer-overflow
bug 等原生内存错误引起的崩溃。如需充分利用此调试功能,请明确启用 GWP-ASan。
后续步骤
继续学习为 Unity 游戏集成 Remote Config Codelab,了解如何在 Unity 中使用 Remote Config 和 A/B 测试。