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 diWebView
è in uso su un dispositivo, vai a Impostazioni > App > Android System WebView e controlla la versione in fondo alla pagina.

WebView
.Pausa di Unity
Quando UnityPlayerActivity
riceve una chiamata onPause()
, inizia la seguente catena di
operazioni:
UnityPlayerActivity
comunica al motore di runtime Unity che l'attività è in pausa.- Unity chiama ogni
MonoBehaviour
che implementa l'eventoOnApplicationPause
. - Unity interrompe i suoi componenti e moduli, come la riproduzione del suono, il rendering, il ciclo di gioco e l'animazione.
- Per assicurarsi che sia
Unity Android Player
(UAP) sia il motore siano sincronizzati, l'UAP attende 4 secondi l'arresto del motore. - 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 unStopwatch
. - Evita operazioni I/O o richieste di rete sincrone.
- Sposta le operazioni in un altro
Thread
utilizzandoTask
. Unity 2023.1 supporta un modello di programmazione asincrona semplificato che utilizza le parole chiave C#async
eawait
.
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.
- Se il tuo codice non dipende dal contesto del thread principale di Unity, utilizza
- 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.

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:
- Debug degli ANR: sviluppo di giochi per Android
- ANR: qualità dell'app