使用進階 Crashlytics 功能瞭解 Unity 遊戲和當機次數

1. 簡介

在本程式碼研究室中,您將瞭解如何使用 Crashlytics 進階功能,進一步掌握當機事件和可能導致當機情形發生的情況。

您將在範例遊戲「MechaHamster:使用 Firebase 版本升級」遊戲。這款範例遊戲是傳統版 Firebase 遊戲 MechaHamster 的新版本,當中移除了大部分的內建 Firebase 功能,讓您有機會在原本的位置導入新的 Firebase 功能。

在遊戲中新增偵錯選單。這個偵錯選單會呼叫您要建立的方法,並讓您執行 Crashlytics 的各項功能。以下方法將說明如何在自動當機報告中加上註解,例如自訂索引鍵、自訂記錄、一般錯誤等。

建構遊戲後,您可以使用偵錯選單並檢查結果,瞭解其提供的獨特檢視畫面,瞭解遊戲公開的運作情況。

課程內容

  • Crashlytics 自動擷取的錯誤類型。
  • 其他可刻意記錄的錯誤。
  • 如何為這些錯誤補充更多資訊,以便理解。

事前準備

  • Unity (最低建議版本 2019 以上版本) 搭配下列一或兩個版本:
    • iOS 版本支援
    • Android 版本支援
  • (僅適用於 Android) Firebase CLI (用來上傳當機報告的符號)

2. 設定開發環境

以下各節將說明如何下載 Level Up with Firebase 程式碼,並在 Unity 中開啟。

請注意,這個「使用 Firebase 升級」範例遊戲已由其他許多 Firebase + Unity 程式碼研究室使用,因此你可能已經完成本節所述的工作。如果是,請直接跳到本頁的最後一個步驟:「新增 Unity 專用 Firebase SDK」。

下載程式碼

從指令列複製本程式碼研究室的 GitHub 存放區

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

如果尚未安裝 Git,可以將存放區下載為 ZIP 檔案

在 Unity 編輯器中開啟「Level Up」

  1. 啟動 Unity Hub,然後在「Projects」分頁中按一下「Open」旁邊的下拉式箭頭
  2. 按一下「從磁碟新增專案」
  3. 前往包含程式碼的目錄,然後按一下「OK」
  4. 畫面出現提示時,請選取要使用的 Unity 編輯器版本,以及您的目標平台 (Android 或 iOS)。
  5. 按一下專案名稱 level-up-with-firebase,然後在 Unity 編輯器中開啟專案。
  6. 如果編輯器沒有自動開啟,請在「Assets」中開啟 MainGameScene >Unity 編輯器中的「Project」分頁中的「Hamster」
    ff4ea3f3c0d29379.png

如要進一步瞭解如何安裝及使用 Unity,請參閱「在 Unity 中使用」。

3. 將 Firebase 新增至您的 Unity 專案

建立 Firebase 專案

  1. Firebase 控制台,按一下「新增專案」
  2. 如要建立新專案,請輸入所需專案名稱。
    這麼做也會將專案 ID (顯示在專案名稱下方) 設為根據專案名稱顯示的內容。您也可以在專案 ID 上按一下「編輯」圖示,進一步自訂專案 ID。
  3. 如果出現提示訊息,請詳閱並接受 Firebase 條款
  4. 按一下 [繼續]
  5. 選取「為這項專案啟用 Google Analytics」選項,然後按一下「繼續」
  6. 選取要使用的現有 Google Analytics 帳戶,或選取「建立新帳戶」來建立新帳戶。
  7. 按一下 [Create Project]
  8. 專案建立完成後,按一下「Continue」

透過 Firebase 註冊應用程式

  1. 同樣在 Firebase 控制台,從專案總覽頁面中間的「Unity」圖示啟動設定工作流程。如果您已將應用程式新增至 Firebase 專案,按一下「新增應用程式」即可顯示平台選項。
  2. 選擇同時註冊 Apple (iOS) 和 Android 建構目標。
  3. 輸入 Unity 專案的平台專屬 ID。在本程式碼研究室中,請輸入以下內容:
    • Apple (iOS):在「iOS 軟體包 ID」欄位中輸入 com.google.firebase.level-up
    • Android:在「Android package name」(Android 套件名稱) 欄位中輸入 com.google.firebase.level_up
  4. (選用) 輸入 Unity 專案的平台專屬暱稱。
  5. 按一下「Register app」,然後前往「Download config file」部分。

新增 Firebase 設定檔

按一下「Register app」後,系統會提示您下載兩個設定檔 (每個建構目標各有一個設定檔)。Unity 專案需要這些檔案中的 Firebase 中繼資料,才能與 Firebase 連結。

  1. 下載兩個可用的設定檔:
    • Apple (iOS):下載 GoogleService-Info.plist
    • Android 裝置:下載 google-services.json
  2. 開啟 Unity 專案的「Project」視窗,然後將兩個設定檔移至「Assets」資料夾中。
  3. 返回 Firebase 控制台,在設定工作流程中點選「Next」,然後繼續新增 Unity 專用 Firebase SDK。

新增 Unity 專用 Firebase SDK

  1. 按一下 Firebase 控制台中的「Download Firebase Unity SDK」(下載 Firebase Unity SDK)
  2. 將 SDK 解壓縮到方便的位置。
  3. 在開放式 Unity 專案中,前往「Assets」>「Import Package」>自訂套件
  4. 在「Import package」(匯入套件) 對話方塊中,前往包含解壓縮 SDK 的目錄,選取 FirebaseAnalytics.unitypackage,然後按一下「Open」
  5. 在隨即顯示的「Import Unity Package」對話方塊中,按一下「Import」
  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(),宣告應用程式變數,然後覆寫方法的實作方式,如下所示:
    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 平台)

  1. 在「Build Settings」對話方塊中,將專案匯出至 Xcode 工作區。
  2. 製作應用程式。
    如果是 Apple 平台,Firebase Unity Editor 外掛程式會自動設定 Xcode 專案,為每個版本產生與 Crashlytics 相容的符號檔案並上傳至 Firebase 伺服器。您必須提供這類符號資訊,才能在 Crashlytics 資訊主頁中查看符號化的堆疊追蹤。

Android

  1. (僅在初始設定期間,而非每個版本) 設定您的版本:
    1. 在專案目錄的根目錄中建立名為「Builds」的新資料夾 (也就是位於「Assets」目錄下的子資料夾),然後建立名為「Android」的子資料夾。
    2. 在「檔案」中 >「Build Settings」>「Player Settings」>設定,將指令碼後端設為 IL2CPP。
      • IL2CPP 一般會使版本較小且效能較佳。
      • IL2CPP 也是 iOS 「唯一」可用的選項。在這裡選取此選項後,兩個平台就能取得更一致的結果,並且簡化兩者的偵錯作業 (選擇同時建構這兩個平台時)。
  2. 建構應用程式。在「檔案」中 >「Build Settings」完成下列設定:
    1. 確認已勾選「Create passwords.zip」 (如果畫面上顯示下拉式選單,請選取「偵錯」)。
    2. 直接從 Unity 編輯器建立 APK,然後在剛才建立的 Builds/Android 子資料夾建立 APK。
  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 資訊主頁!資訊主頁位於資訊主頁底部的「問題」表格中。在程式碼研究室的後續部分,您將進一步瞭解如何探索這些報表。
  5. 確認事件已上傳至 Crashlytics 後,請選取附加至 Crashlytics 的「EmptyObject」 GameObject、只移除 CrashlyticsTester 元件,然後儲存場景,將情境還原到原始條件。

5. 啟用並瞭解偵錯選單

到目前為止,您已將 Crashlytics 新增到 Unity 專案、完成設定,並確認 Crashlytics SDK 正在將事件上傳至 Firebase。您將在 Unity 專案中建立選單,示範如何在遊戲中使用更進階的 Crashlytics 功能。Level Up with Firebase Unity 專案已有隱藏的偵錯選單,方便您查看及編寫這項功能。

啟用偵錯選單

用於存取偵錯選單的按鈕位於 Unity 專案中,但目前尚未啟用。您必須從 MainMenu 預先流程啟用該按鈕,才能使用該按鈕:

  1. 在 Unity 編輯器中,開啟名為 MainMenu 的 Prefab。4148538cbe9f36c5.png
  2. 在 Prefab 階層中,找出名為 DebugMenuButton 的已停用子物件,然後選取該子物件。816f8f9366280f6c.png
  3. 勾選包含「DebugMenuButton」的文字欄位左上角的方塊,即可啟用 DebugMenuButton8a8089d2b4886da2.png
  4. 儲存 Prefab
  5. 在編輯器或裝置上執行遊戲。現在應可存取選單。

預覽及瞭解「Debug」選單的方法主體

稍後在本程式碼研究室中,您將針對一些預先設定的偵錯 Crashlytics 方法編寫方法主體。不過,在「Level Up with Firebase」Unity 專案中,方法是在 DebugMenu.cs 中定義並從中呼叫。

雖然其中有些方法都會呼叫 Crashlytics 方法並擲回錯誤,但 Crashlytics 能夠擷取這些錯誤,不需要先呼叫這些方法。相反地,利用這些方法新增的資訊,可以自動擷取錯誤而產生的當機報告。

開啟 DebugMenu.cs,然後找出下列方法:

產生 Crashlytics 問題並加上註解的方法:

  • CrashNow
  • LogNonfatalError
  • LogStringsAndCrashNow
  • SetAndOverwriteCustomKeyThenCrash
  • SetLogsAndKeysBeforeANR

記錄 Analytics 事件以協助偵錯的方法:

  • LogProgressEventWithStringLiterals
  • LogIntScoreWithBuiltInEventAndParams

在本程式碼研究室的後續步驟中,您將實作這些方法,並瞭解這些方法如何協助解決遊戲開發可能發生的特定情況。

6. 確保在開發期間傳送當機報告

開始導入這些偵錯方法,並瞭解這些方法對當機報告的影響之前,請務必先瞭解事件回報給 Crashlytics 的方式。

如果是 Unity 專案,遊戲中的當機和例外狀況事件會立即寫入磁碟。針對不會導致遊戲當機的未偵測到例外狀況 (例如遊戲邏輯中未偵測到的 C# 例外狀況),您可以將 Crashlytics.ReportUncaughtExceptionsAsFatal 屬性設為 true,讓 Crashlytics SDK 將這些事件回報為嚴重事件,並在 Unity 專案中初始化 Crashlytics。這些事件會即時回報給 Crashlytics,使用者不需重新啟動遊戲。請注意,系統一律會將原生當機事件回報為嚴重事件,並在使用者重新啟動遊戲時傳送。

此外,請留意下列微小但明顯的差異,說明不同執行階段環境傳送 Crashlytics 資訊給 Firebase 的方式差異:

iOS 模擬器:

  • 只有在您將 Xcode 從模擬器中卸離 Xcode 時,系統才會回報 Crashlytics 資訊。如果附加的是 Xcode,則會在上游擷取錯誤,防止資訊傳送。

行動裝置實體裝置 (Android 和 iOS):

  • Android 專屬:ANR 回報次數僅適用於 Android 11 以上版本。系統會在下次執行時回報 ANR 和一般事件。

Unity 編輯器:

測試在 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. 輕觸「Crash Now」按鈕,然後繼續進行本程式碼研究室的下一個步驟,瞭解如何查看及解讀當機報告。

7. 瞭解 Firebase 控制台的問題報表

您在查看當機報告時,還需要知道如何充分利用這些報表。無論您的編寫方法為何,都會說明如何在 Crashlytics 報表中新增不同類型的資訊。

  1. 輕觸「Crash Now」按鈕,然後重新啟動應用程式。
  2. 前往 Crashlytics 資訊主頁。向下捲動至資訊主頁底部的「問題」表格,Crashlytics 會將具有相同根本原因的事件歸類為「問題」。
  3. 按一下「問題表格中所列的新問題。這樣就能針對傳送至 Firebase 的各項事件,顯示其事件摘要

    畫面應該會如下所示。請留意事件摘要會以顯眼的方式呈現造成當機的呼叫堆疊追蹤。40c96abe7f90c3aa.png

其他中繼資料

另一個實用的分頁是「Unity Metadata」(Unity 中繼資料) 分頁。本節說明事件發生的裝置屬性,包括實體功能、CPU 型號/規格,以及各種 GPU 指標。

以下是這個分頁中的資訊可能有幫助的例子:
假設遊戲大量使用著色器來呈現特定外觀,但並非所有手機都具備可轉譯這項功能的 GPU。透過「Unity Metadata」(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 資訊主頁,您應該會看到類似本程式碼研究室最後一個步驟的步驟。
  7. 不過,這次請將「Event type」(事件類型) 篩選器設為「Non-fatals」,這樣您就只會看到不嚴重的錯誤,例如您剛記錄的錯誤。
    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. 輕觸「Log Strings and Crash Now」按鈕,然後重新啟動應用程式。
  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

應用程式無回應 (ANR) 錯誤是為 Android 開發人員偵錯的最難問題之一。如果應用程式無法回應輸入超過 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. 輕觸標示為「設定記錄和金鑰」→「ANR」的按鈕,然後重新啟動應用程式。
  5. 返回 Crashlytics 資訊主頁,然後在「問題」表格中點選新問題,即可查看事件摘要。如果呼叫順利,您應該會看到類似下方的畫面:
    876c3cff7037bd07.png

    如您所見,Firebase 將執行緒上的忙碌等待時間視為應用程式觸發 ANR 的主要原因。
  6. 如果查看「事件摘要」中「記錄」分頁中的記錄,就會發現最後一個記錄為完成的方法為 DoSevereWork
    5a4bec1cf06f6984.png

    相對來說,最後一個列為「啟動」的方法為 DoExtremeWork,代表在此方法中發生 ANR,而遊戲在記錄 DoExtremeWork 前就關閉。

    89d86d5f598ecf3a.png

為什麼這樣做?

  • 重現 ANR 並不容易,因此能夠取得程式碼區域和指標的詳盡資訊相當重要。
  • 透過儲存在自訂鍵中的資訊,您現在可以知道哪個非同步執行緒的執行時間最長,以及哪些非同步執行緒可能會觸發 ANR。這類相關的邏輯和數值資料,可讓您瞭解程式碼中最需要改進的部分。

12. 交錯分析事件,進一步充實報表內容

下列方法也可從偵錯選單呼叫,不過並不是自行產生問題,而是使用 Google Analytics 做為另一種資訊來源,讓您進一步瞭解遊戲成效。

與您在本程式碼研究室中編寫的其他方法不同,您應將這些方法與其他方法搭配使用。視需要任意順序呼叫這些方法 (在偵錯選單中按下對應的按鈕),再執行其他方法。之後查看特定 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. 按下「Crash Now」按鈕。
  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 遊戲」程式碼研究室,瞭解如何在 Unity 中使用遠端設定和 A/B 測試。