Unity ANR 的發生原因有很多,最常見的 ANR 是因誤用 Android 和 Unity 元件,以及兩者間的通訊錯誤所致。
WebView
WebView
是 Android 類別,可顯示網頁。第三方 SDK (例如廣告) 會使用 WebView
在 UnityPlayerActivity
以外的活動中顯示動態網頁內容。第三方 SDK 濫用 WebView
時,就會發生 ANR。
堆疊追蹤
堆疊追蹤是瞭解 ANR 原因的第一個方法。
/data/app/~~p-0ksfCD6bF6Sdq6kpVePg==/com.google.android.webview-5YQZOqKbbqp-uoLY6WYnTw==/base.apk!libmonochrome.so
at J.N.Mhc_M_H$ (Native method)
at org.chromium.components.viz.service.frame_sinks.ExternalBeginFrameSourceAndroid.doFrame (chromium-TrichromeWebViewGoogle.aab-stable-579013831:60)
at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1054)
at android.view.Choreographer.doCallbacks (Choreographer.java:878)
at android.view.Choreographer.doFrame (Choreographer.java:807)
at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1041)
at android.os.Handler.handleCallback (Handler.java:938)
at android.os.Handler.dispatchMessage (Handler.java:99)
at android.os.Looper.loop (Looper.java:223)
at android.app.ActivityThread.main (ActivityThread.java:7721)
at java.lang.reflect.Method.invoke (Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:952)
圖 1. 因 futex 等候而導致的 ANR 堆疊追蹤。
原因
目前我們尚未找出這個問題的根本原因。可能原因包括:
- 廣告導入方式不當。
WebView
版本過舊,因為使用者可能選擇不自動更新應用程式。- 系統資源 (CPU、GPU 等) 使用率過高,可能需要大量剖析。
- 著色器編譯當機,可能表示內容含有不相容的著色器,或使用者安裝了舊版
WebView
。
解決方案
- 如要縮小範圍,找出導致
WebView
封鎖主執行緒的內容類型,請在載入、顯示或關閉網頁時,將記錄檔新增至遊戲。- 您可以使用 Backtrace 或 Crashlytics 報表服務。
- 接著,在分析資料並找出問題後,請嘗試停用違規的廣告供應商。
- 附上記憶體記錄,確保問題與記憶體無關。
- 提醒使用者從 Google Play 更新
WebView
。從 Android 5.0 (API 級別 21) 以上版本開始,WebView
已移至 APK。因此,這項元件可以與 Android 平台分開更新。如要查看裝置使用的WebView
版本,請依序前往「設定」>「應用程式」>「Android 系統 WebView」,然後查看頁面底部的版本。

WebView
版本。暫停 Unity
UnityPlayerActivity
收到 onPause()
呼叫時,會啟動下列作業鏈:
UnityPlayerActivity
會通知 Unity 執行階段引擎活動已暫停。- Unity 會呼叫實作
OnApplicationPause
事件的每個MonoBehaviour
。 - Unity 會停止元件和模組,例如音效播放、算繪、遊戲迴圈和動畫。
- 為確保
Unity Android Player
(UAP) 和引擎同步,UAP 會等待 4 秒,讓引擎停止運作。 - 如果該作業耗時超過 5 秒,系統就會觸發 ANR。
堆疊追蹤
"main" tid=1 Timed Waiting
jdk.internal.misc.Unsafe.park (Native method)
java.util.concurrent.locks.LockSupport.parkNanos (LockSupport.java:234)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos (AbstractQueuedSynchronizer.java:1079)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos (AbstractQueuedSynchronizer.java:1369)
java.util.concurrent.Semaphore.tryAcquire (Semaphore.java:415)
com.unity3d.player.UnityPlayer.pauseUnity (UnityPlayer.java:833)
com.unity3d.player.UnityPlayer.pause (UnityPlayer.java:796)
com.unity3d.player.UnityPlayerActivity.onPause (UnityPlayerActivity.java:117)
android.app.Activity.performPause (Activity.java:8517)
android.app.Instrumentation.callActivityOnPause (Instrumentation.java:1618)
android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5061)
android.app.ActivityThread.performPauseActivity (ActivityThread.java:5022)
android.app.ActivityThread.handlePauseActivity (ActivityThread.java:4974)
android.app.servertransaction.PauseActivityItem.execute (PauseActivityItem.java:48)
android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45)
android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:179)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:97)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2303)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:201)
android.os.Looper.loop (Looper.java:288)
android.app.ActivityThread.main (ActivityThread.java:7884)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)
圖 3. 信號燈未釋出而導致 ANR。
解決方案
請確保 C# 遊戲程式碼不會在暫停或繼續事件期間,花費太長時間完成執行作業。
- 分析遊戲並檢查
OnApplicationPause
是否為耗用資源的作業。你可以使用Stopwatch
。 - 避免 I/O 作業或同步網路要求。
- 使用
Task
將作業移至另一個Thread
。Unity 2023.1 支援使用 C#async
和await
關鍵字,簡化非同步程式設計模型。
UnitySendMessage 已封鎖
Java Unity 外掛程式和 SDK 會使用 JNI 將資料傳送至 C# 遊戲層。不過,由於互斥鎖等原生同步處理常式,這項通訊可能會封鎖主執行緒,導致鎖定爭用而引發 ANR。
堆疊追蹤
圖 4 中的 ANR 是由 Java 外掛程式呼叫的 C# 程式碼中,長時間執行的作業所造成。Unity 引擎會使用非優先順序繼承互斥鎖,確保正確執行。
libc.so NonPI::MutexLockWithTimeout(pthread_mutex_internal_t*, bool, timespec const*) + 604
com.unity3d.player.UnityPlayer.nativeUnitySendMessage (Native method)
com.unity3d.player.UnityPlayer.UnitySendMessage (UnityPlayer.java:665)
圖 4. 因鎖定爭用造成的 ANR。
原因
問題是應用程式恢復時會傳送多則訊息。遊戲在背景執行時無法傳送訊息,因此系統會將訊息排入佇列。應用程式恢復時,所有訊息會同時傳送。
暫停期間,您通常會將遊戲資訊儲存在伺服器上;舉例來說,您會記錄玩家在遊戲中的位置,以便玩家在遊戲恢復時回到相同位置。
這項工作負載加上其他第三方程式碼建立的工作負載,可能會導致裝置資源過載,尤其是主執行緒。主要執行緒會執行應用程式的使用者介面,通常也是發生 ANR 的主要位置。因此,主執行緒上新增的任何工作負載,都會增加發生 ANR 的可能性。
解決方案
應用程式暫停時,請確認所有程式碼動作都是必要的,或嘗試將使用者狀態儲存在本機裝置記憶體中。當然,您也可以查看是否能在暫停期間外完成這些動作。
幾種做法:
- 將處理 訊息 的 C# 作業移至主執行緒以外的執行緒。
- 如果程式碼不依附於 Unity 的主要執行緒環境,請使用
Task
進行通訊,而非訊息。
- 如果程式碼不依附於 Unity 的主要執行緒環境,請使用
- 遊戲暫停時,請勿從外掛程式傳送多則訊息。
- 遊戲在背景執行時,引擎無法傳送訊息。
- 如果只傳送最後的資料狀態不會影響遊戲功能,請只傳送最後的資料狀態。
安裝參照網址
Play 安裝參照網址是使用者點按廣告時傳送至 Play 商店的專屬字串。這是 Android 專用的廣告追蹤 ID,安裝完成後,應用程式會將安裝推薦連結傳送給歸因合作夥伴,後者會將來源與安裝事件相符 (歸因轉換)。
堆疊追蹤
圖 5 顯示遊戲的 ANR 堆疊追蹤記錄,該遊戲使用 Facebook SDK 擷取安裝歸因。

原因
ANR 發生原因是 Binder 呼叫速度緩慢。不過,如要判斷根本原因,必須存取 SDK 原始碼。
解決方案
解決這類問題需要與 SDK 開發人員溝通,或在網路上搜尋可能的解決方案,確認較新版本的 SDK 是否能解決其他人的 ANR 問題,甚至嘗試採用小規模的推出策略。
Google 提供 SDK 索引頁面,彙整了來自 Google Play 應用程式的使用資料和透過偵測程式碼收集到的資訊,讓您可以根據相關屬性和信號,決定是否在應用程式中採用、保留或移除 SDK。
其他資源
如要進一步瞭解 ANR,請參閱下列資源: