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

1. 簡介

在本程式碼研究室中,您將瞭解如何使用 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 中開啟。

請注意,其他幾個 Firebase + Unity 程式碼研究室也會使用這個「Level Up with Firebase」範例遊戲,因此您可能已完成本節中的工作。如果是,您可以直接前往本頁的最後一個步驟:「新增適用於 Unity 的 Firebase SDK」。

下載程式碼

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

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

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

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

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

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

3. 將 Firebase 新增至 Unity 專案

建立 Firebase 專案

  1. 使用 Google 帳戶登入 Firebase 控制台
  2. 按一下按鈕建立新專案,然後輸入專案名稱 (例如 Mechahamster Codelab)。
  3. 按一下「繼續」
  4. 如果系統提示,請詳閱並接受 Firebase 條款,然後按一下「繼續」
  5. (選用) 在 Firebase 控制台中啟用 AI 輔助功能 (稱為「Gemini in Firebase」)。
  6. 在本程式碼研究室中,您需要 Google Analytics 才能充分運用 Firebase 產品,因此請開啟 Google Analytics 選項的切換按鈕。按照畫面上的指示設定 Google Analytics。
  7. 按一下「建立專案」,等待專案佈建完成,然後按一下「繼續」

向 Firebase 註冊應用程式

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

新增 Firebase 設定檔

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

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

新增 Firebase Unity SDK

  1. 在 Firebase 主控台中,按一下「下載 Firebase Unity SDK」
  2. 將 SDK 解壓縮至方便使用的位置。
  3. 在開啟的 Unity 專案中,依序前往「Assets」 >「Import Package」 >「Custom 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 編輯器外掛程式會自動設定 Xcode 專案,為每個建構作業產生並上傳與 Crashlytics 相容的符號檔案至 Firebase 伺服器。您必須提供這些符號資訊,才能在 Crashlytics 資訊主頁中查看符號化的堆疊追蹤。

Android

  1. (僅限初始設定期間,不適用於每個版本)設定版本:
    1. 在專案目錄的根層級 (即「Assets」目錄的同層級) 建立名為「Builds」的新資料夾,然後建立名為「Android」的子資料夾。
    2. 依序前往「File」>「Build Settings」>「Player Settings」>「Configuration」,將「Scripting Backend」設為「IL2CPP」。
      • 一般來說,IL2CPP 會縮小建構大小並提升效能。
      • IL2CPP 也是 iOS 上唯一可用的選項,在此選取這個選項可讓這兩個平台更趨於一致,並簡化兩者之間的偵錯差異 (如果您選擇同時建構兩者)。
  2. 建構應用程式。在「File」 >「Build Settings」中,完成下列步驟:
    1. 請務必勾選「建立 symbols.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」目錄中產生的符號 ZIP 檔案路徑 (例如: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 後,請選取您附加事件的「EmptyObject」 GameObject,只移除 CrashlyticsTester 元件,然後儲存場景,將其還原為原始狀態。

5. 啟用及瞭解偵錯選單

到目前為止,您已將 Crashlytics 新增至 Unity 專案、完成設定,並確認 Crashlytics SDK 會將事件上傳至 Firebase。您現在要在 Unity 專案中建立選單,示範如何在遊戲中使用更進階的 Crashlytics 功能。「Level Up with Firebase」Unity 專案已具備隱藏的「Debug Menu」,您將顯示這個選單並編寫相關功能。

啟用偵錯選單

Unity 專案中設有存取「偵錯選單」的按鈕,但目前未啟用。您必須啟用按鈕,才能從 MainMenu 預先設定的項目存取按鈕:

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

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

在本程式碼研究室的後續部分,您將為一些預先設定的 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 從模擬器中分離時,系統才會回報 Crashlytics 資訊。如果附加 Xcode,Xcode 會在上游擷取錯誤,防止資訊傳送。

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

  • Android 專屬:系統只會回報 Android 11 以上版本發生的 ANR。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. 輕觸「立即異常終止」按鈕,然後重新啟動應用程式。
  2. 前往 Crashlytics 資訊主頁。向下捲動至資訊主頁底部的「問題」表格,Crashlytics 會將根本原因相同的事件歸類為「問題」。
  3. 按一下「問題」表格中列出的新問題。這樣做會顯示傳送至 Firebase 的每個個別事件的「事件摘要」

    您應該會看到類似下方的螢幕截圖。請注意,事件摘要會醒目顯示導致當機的呼叫堆疊追蹤記錄。40c96abe7f90c3aa.png

其他中繼資料

另一個實用的分頁是「Unity Metadata」(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. 輕觸「Log Nonfatal Error」(記錄非嚴重錯誤) 按鈕,然後重新啟動應用程式。
  6. 前往 Crashlytics 資訊主頁,您應該會看到類似於本程式碼研究室上一個步驟的內容。
  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. 輕觸「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. 輕觸「Set Custom Key and Crash」按鈕,然後重新啟動應用程式。
  5. 返回 Crashlytics 資訊主頁,然後點按「問題」表格中列出的最新問題。您應該會再次看到類似先前問題的內容。
  6. 不過,這次請點選「事件摘要」中的「鍵」分頁,查看鍵的值,包括 Current Time
    7dbe1eb00566af98.png

為什麼要使用自訂金鑰,而不是自訂記錄?

  • 記錄適合儲存序列資料,但如果您只需要最新值,自訂鍵會是更好的選擇。
  • 在 Firebase 控制台中,您可以在「問題」表格的搜尋框中,依據鍵的值輕鬆篩選問題。

不過,自訂鍵與記錄類似,都有數量限制。Crashlytics 最多支援 64 個鍵/值組合。達到這個門檻後,系統就不會再儲存其他值。每個鍵/值組的大小上限為 1 KB。

11. (僅限 Android) 使用自訂金鑰和記錄,瞭解及診斷 ANR

對 Android 開發人員來說,最難偵錯的問題類別之一就是「應用程式無回應」(ANR) 錯誤。如果應用程式未在 5 秒內回應輸入事件,就會發生 ANR。這代表應用程式凍結或速度緩慢。系統會向使用者顯示對話方塊,讓他們選擇「等待」或「關閉應用程式」。

ANR 會造成不良的使用者體驗,且如上述 ANR 連結所述,可能會影響應用程式在 Google Play 商店的曝光度。由於 ANR 複雜,且通常是由多執行緒程式碼所致,不同手機型號的行為差異極大,因此在偵錯時,重現 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 來說非常重要。
  • 有了儲存在自訂鍵中的資訊,您現在可以瞭解哪個非同步執行緒的執行時間最長,以及哪些執行緒可能觸發 ANR。這類相關的邏輯和數值資料會顯示程式碼中最需要最佳化的部分。

12. 穿插 Analytics 事件,進一步豐富報表內容

下列方法也可從「偵錯」選單呼叫,但這些方法不會自行產生問題,而是使用 Google Analytics 做為另一個資訊來源,進一步瞭解遊戲的運作方式。

與您在本程式碼研究室中編寫的其他方法不同,您應該將這些方法與其他方法搭配使用。在執行其他方法之前,請以任意順序呼叫這些方法 (按下「Debug Menu」中的對應按鈕)。接著,當您檢查特定 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. 建構及部署遊戲,然後進入「Debug Menu」
  3. (僅限 Android) 執行下列 Firebase CLI 指令,上傳符號:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 按一下下列至少一個按鈕,即可呼叫上述功能:
    • 記錄字串事件
    • 記錄 Int 事件
  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 遊戲進行插碼」程式碼研究室,瞭解如何在 Unity 中使用遠端設定和 A/B 測試。