Gli errori ANR Unity si verificano per diversi motivi. Gli errori ANR più comuni sono causati da uso improprio dei componenti di Android e Unity e dei relativi problemi di comunicazione.
WebView
WebView
è un corso Android che mostra pagine web. Di terze parti
Gli SDK (ad esempio gli annunci) utilizzano WebView
per visualizzare contenuti web dinamici
in attività diverse da UnityPlayerActivity
. Gli errori ANR si verificano quando
Gli SDK fanno un uso improprio di WebView
.
Analisi dello stack
L'analisi dello stack è il primo ricorso per comprendere la causa dell'errore 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
Al momento, la causa principale di questo problema non è chiara. Alcune potenziali cause include:
- Implementazione errata degli annunci.
- Una versione obsoleta di
WebView
perché l'utente potrebbe aver scelto di non eseguire l'aggiornamento l'app automaticamente. - Utilizzo elevato di risorse di sistema (CPU, GPU, ecc.), che può richiedere la profilazione.
- La compilazione Shader ha un arresto anomalo, il che potrebbe indicare che
i contenuti hanno uno shaker incompatibile o che l'utente ha un vecchio
WebView
installata.
Soluzione
- Per restringere il tipo di contenuti che causano il blocco da parte di
WebView
thread principale, aggiungi log al tuo gioco ogni volta che una pagina web viene caricata, visualizzata o chiuso.- Puoi utilizzare Backtrace o Crashlytics. servizi di reporting.
- Dopodiché, dopo aver analizzato i dati e aver individuato il problema, prova a disattivare lo trasgressori.
- Includi i log della memoria per assicurarti che il problema non sia correlato alla memoria.
- Avvisa l'utente di aggiornare
WebView
da Google Play. Da Android 5.0 (livello API 21) e versioni successive,WebView
è stato spostato in un APK. Pertanto, può essere aggiornati separatamente dalla piattaforma Android. Per visualizzare quale versione diWebView
è in uso su un dispositivo, vai a Impostazioni > App > Sistema Android WebView e la versione in fondo alla pagina.
Pausa del formato Unity
Quando UnityPlayerActivity
riceve una chiamata onPause()
, la seguente catena di
dell'avvio delle operazioni:
UnityPlayerActivity
comunica al motore di runtime Unity che l'attività è in pausa.- Unity chiama ogni
MonoBehaviour
che implementa la funzioneOnApplicationPause
. - Unity interrompe i suoi componenti e moduli, come riproduzione audio, rendering, ciclo di gioco e animazione.
- Per assicurarti che sia
Unity Android Player
(UAP) sia il motore sono sincronizzati, l'UAP attende 4 secondi per 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. Errore ANR causato da un semaforo mai pubblicato.
Soluzione
Assicurati che l'esecuzione del codice di gioco C# non richieda troppo tempo durante un metti in pausa o riprendi l'evento.
- Profila il tuo gioco e controlla se il
OnApplicationPause
è costoso operativa. Puoi usare unStopwatch
. - Evita operazioni di I/O o richieste di rete sincrone.
- Sposta le operazioni in un'altra
Thread
utilizzando il metodoTask
. Unity 2023.1 supporta una versione modello di programmazione asincrona utilizzando C#async
eawait
parole chiave.
UnitySendMessage bloccato
Gli SDK e i plug-in Java Unity inviano i dati al livello del gioco C# utilizzando JNI. Tuttavia, questa comunicazione potrebbe bloccare il thread principale a causa di un errore di una routine di sincronizzazione, come un mutex, che causa un errore ANR a causa del conflitto del blocco.
Analisi dello stack
L'errore ANR nella figura 4 è causato da un'operazione lunga nel codice C# chiamata da un Plug-in Java. Il motore Unity utilizza un'ereditarietà non prioritaria mutex per garantire la corretta esecuzione.
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. Errore ANR causato da un conflitto di blocchi.
Causa
Il problema è che vengono inviati diversi messaggi quando l'applicazione viene ripreso. I messaggi sono in coda perché non possono essere inviati durante la partita è in background. I messaggi vengono inviati tutti contemporaneamente quando e l'app riprenderà.
Durante un periodo di pausa, in genere memorizzi le informazioni del gioco nella server; ad esempio, registri la posizione di un giocatore nel gioco affinché quest'ultimo può tornare nello stesso luogo quando la partita 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 principale il thread esegue l'interfaccia utente di un'app ed è spesso la posizione principale degli ANR. Quindi, qualsiasi carico di lavoro aggiunto al thread principale aumenta il potenziale di un errore ANR.
Soluzione
Durante la pausa di un'applicazione, assicurati che tutte le azioni relative al codice siano necessarie oppure prova a salvare lo stato dell'utente nella memoria locale del dispositivo. E, naturalmente, vedi se puoi completare queste azioni anche al di fuori del periodo di pausa.
Alcuni approcci:
- Spostare l'operazione C# che gestisce un messaggio in un thread
diversi dal thread principale.
- Se il tuo codice non dipende dal contesto del thread principale di Unity, usa
Task
per la comunicazione anziché per il messaggio.
- Se il tuo codice non dipende dal contesto del thread principale di Unity, usa
- Non inviare più messaggi dal tuo plug-in quando il gioco è in pausa.
- Il motore non può inviare messaggi mentre il gioco è in background.
- Invia al tuo gioco l'ultimo stato dei dati solo se questo non influisce su quest'ultimo funzionalità.
Installa referrer
Play Install Referrer è una stringa univoca inviata al Play Store ogni volta che un un utente fa clic su un annuncio. Si tratta di un identificatore di monitoraggio degli annunci specifico di Android. Una volta installata, l'app invia il referrer di installazione al partner di attribuzione, che corrisponde alla sorgente all'installazione (attribuisce la conversione).
Analisi dello stack
La figura 5 mostra un'analisi dello stack ANR di un gioco che utilizza l'SDK di Facebook per recuperare l'attribuzione delle installazioni.
Causa
L'errore ANR è stato causato da una chiamata a binder lenta. Tuttavia, la causa principale non può essere determinato senza accesso al codice sorgente dell'SDK.
Soluzione
Per risolvere questo tipo di problema è necessario comunicare con lo sviluppatore dell'SDK o spesso cercano online una potenziale soluzione, controllando se un nuovo dell'SDK risolve l'errore ANR per altri o anche sperimentando con un di lancio.
Google fornisce una pagina SDK Index che combina i dati sull'utilizzo dalle app Google Play con 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 scoprire di più sugli errori ANR, consulta le seguenti risorse:
- ANR di debug: sviluppo di giochi Android
- ANR - Qualità dell'app