Errori ANR comuni dei giochi Unity

Gli errori ANR di Unity si verificano per diversi motivi. Gli errori ANR più comuni sono causati da un uso improprio dei componenti di Android e Unity e dalla loro comunicazione errata.

WebView

WebView è una classe Android che mostra le pagine web. Gli SDK di terze parti (ad esempio gli annunci) utilizzano WebView per visualizzare contenuti web dinamici in attività diverse da UnityPlayerActivity. Gli errori ANR si verificano quando gli SDK di terze parti utilizzano in modo improprio WebView.

Analisi dello stack

Lo stack trace è il tuo primo ricorso per comprendere la causa dell'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)

Figura 1.Analisi dello stack ANR causata da un'attesa futex.

Causa

Finora, la causa principale del problema non è chiara. Alcune possibili cause includono:

  • Implementazione errata degli annunci.
  • Una versione obsoleta di WebView, poiché l'utente potrebbe aver scelto di non aggiornare l'app automaticamente.
  • Utilizzo elevato delle risorse di sistema (CPU, GPU e così via), che potrebbe richiedere una profilazione approfondita.
  • Arresti anomali della compilazione degli shader, che potrebbero indicare che i contenuti hanno uno shader incompatibile o che l'utente ha installato una versione precedente di WebView.

Soluzione

  • Per restringere il tipo di contenuti che causano il blocco del thread principale da parte di WebView, aggiungi log al tuo gioco ogni volta che una pagina web viene caricata, visualizzata o chiusa.
    • Puoi utilizzare i servizi di reporting Backtrace o Crashlytics.
    • Dopodiché, dopo aver analizzato i dati e individuato il problema, prova a disattivare i fornitori di annunci problematici.
    • Includi i log di memoria per assicurarti che il problema non sia correlato alla memoria.
  • Avvisa l'utente di aggiornare WebView da Google Play. A partire da Android 5.0 (livello API 21) e versioni successive, WebView è stato spostato in un APK. Pertanto, può essere aggiornato separatamente dalla piattaforma Android. Per vedere quale versione di WebView è in uso su un dispositivo, vai a Impostazioni > App > Android System WebView e controlla la versione in fondo alla pagina.
Schermata Informazioni app che mostra le versioni di WebView.
Figura 1. Controlla la versione di WebView.

Pausa di Unity

Quando UnityPlayerActivity riceve una chiamata onPause(), inizia la seguente catena di operazioni:

  1. UnityPlayerActivity comunica al motore di runtime Unity che l'attività è in pausa.
  2. Unity chiama ogni MonoBehaviour che implementa l'evento OnApplicationPause.
  3. Unity interrompe i suoi componenti e moduli, come la riproduzione del suono, il rendering, il ciclo di gioco e l'animazione.
  4. Per assicurarsi che sia Unity Android Player (UAP) sia il motore siano sincronizzati, l'UAP attende 4 secondi l'arresto del motore.
  5. Se l'operazione richiede più di 5 secondi, il sistema attiva un errore ANR.

Analisi dello stack

"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)

Figura 3. ANR causato da un semaforo mai rilasciato.

Soluzione

Assicurati che il codice di gioco C# non impieghi troppo tempo per terminare l'esecuzione durante un evento di pausa o ripresa.

  • Esegui la profilazione del gioco e controlla se OnApplicationPause è un'operazione costosa. Puoi utilizzare un Stopwatch.
  • Evita operazioni I/O o richieste di rete sincrone.
  • Sposta le operazioni in un altro Thread utilizzando Task. Unity 2023.1 supporta un modello di programmazione asincrona semplificato che utilizza le parole chiave C# async e await.

UnitySendMessage bloccato

I plug-in e gli SDK Java Unity inviano dati al livello di gioco C# utilizzando JNI. Tuttavia, questa comunicazione potrebbe bloccare il thread principale a causa di una routine di sincronizzazione nativa, ad esempio un mutex, causando un errore ANR dovuto alla contesa del blocco.

Analisi dello stack

L'ANR nella Figura 4 è stato causato da un'operazione lunga nel codice C# chiamato da un plug-in Java. Il motore Unity utilizza un mutex di ereditarietà non prioritaria per garantire l'esecuzione corretta.

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)

Figura 4. ANR causato da un conflitto di blocco.

Causa

Il problema è che vengono inviati diversi messaggi quando l'applicazione viene ripristinata. I messaggi vengono messi in coda perché non possono essere inviati mentre il gioco è in background. I messaggi vengono inviati contemporaneamente quando l'app riprende l'attività.

Durante un periodo di pausa, in genere memorizzi le informazioni del gioco sul server; ad esempio, registri la posizione di un giocatore nel gioco in modo che possa tornare nello stesso punto quando il gioco riprende.

Questo carico di lavoro, combinato con altro codice di terze parti che crea il proprio carico di lavoro, può sovraccaricare le risorse del dispositivo, in particolare il thread principale. Il thread principale esegue l'interfaccia utente di un'app ed è spesso la posizione principale degli errori ANR. Pertanto, qualsiasi carico di lavoro aggiunto al thread principale aumenta la possibilità di un errore ANR.

Soluzione

Durante una pausa dell'applicazione, assicurati che tutte le azioni del codice siano necessarie oppure prova a salvare lo stato dell'utente nella memoria del dispositivo locale. e, ovviamente, vedere se puoi completare queste azioni anche al di fuori del periodo di pausa.

Alcuni approcci:

  • Sposta l'operazione C# che gestisce un messaggio in un thread diverso dal thread principale.
    • Se il tuo codice non dipende dal contesto del thread principale di Unity, utilizza Task per la comunicazione anziché il messaggio.
  • Non inviare più messaggi dal plug-in quando il gioco è in pausa.
    • Il motore non può inviare messaggi mentre il gioco è in background.
    • Invia al gioco solo l'ultimo stato dei dati se ciò non influisce sulla funzionalità del gioco.

Installazione di Referrer

Play Install Referrer è una stringa univoca inviata al Play Store ogni volta che un utente fa clic su un annuncio. È un identificatore di monitoraggio degli annunci specifico per Android. Una volta installata, l'app invia il referrer dell'installazione al partner di attribuzione, che corrisponde all'origine con l'installazione (attribuendo la conversione).

Analisi dello stack

La Figura 5 mostra uno stack trace ANR di un gioco che utilizza l'SDK Facebook per recuperare l'attribuzione dell'installazione.

Figura 5. Report Android vitals contenente una chiamata Binder.

Causa

L'ANR è stato causato da una chiamata a Binder lenta. Tuttavia, la causa principale non può essere determinata senza l'accesso al codice sorgente dell'SDK.

Soluzione

La risoluzione di questo tipo di problema comporta la comunicazione con lo sviluppatore dell'SDK o molte ricerche online per trovare una potenziale soluzione, controllare se una versione più recente dell'SDK risolve l'errore ANR per altri o persino sperimentare una piccola strategia di implementazione.

Google fornisce una pagina dell'indice SDK che combina i dati sull'utilizzo delle app di Google Play con le informazioni raccolte tramite il rilevamento del codice per fornire attributi e indicatori pensati per aiutarti a decidere se adottare, mantenere o rimuovere un SDK dalla tua app.

Risorse aggiuntive

Per saperne di più sugli errori ANR, consulta le seguenti risorse: