Quando il thread dell'interfaccia utente di un'app per Android viene bloccato per troppo tempo, il sistema invia un errore "L'applicazione non risponde" (ANR). Questa pagina descrive i diversi tipi di ANR, come diagnosticarli e suggerimenti per risolverli. Tutti gli intervalli di tempo di timeout predefiniti elencati sono per i dispositivi AOSP e Pixel; questi tempi possono variare in base all'OEM.
Tieni presente che, quando determini la causa degli errori ANR, è utile distinguere tra problemi di sistema e problemi delle app.
Quando il sistema è in uno stato anomalo, i seguenti problemi possono causare errori ANR:
- I problemi temporanei nel server di sistema causano in genere chiamate binder veloci lente.
- Problemi con il server di sistema e carico elevato del dispositivo causano la mancata pianificazione dei thread dell'app.
Se disponibile, un buon modo per distinguere i problemi di sistema da quelli delle app è utilizzare le tracce Perfetto:
- Controlla se il thread principale dell'app è pianificato esaminando la traccia dello stato del thread in Perfetto per vedere se è in esecuzione o eseguibile.
- Esamina i thread
system_serverper problemi come la contesa di blocchi. - Per le chiamate lente del binder, esamina il thread di risposta, se presente, per capire perché è lento.
Input timeout di invio
Gli errori ANR di distribuzione dell'input si verificano quando il thread principale dell'app non risponde in tempo a un evento di input, ad esempio uno scorrimento o la pressione di un tasto. Poiché l'app è in primo piano quando si verificano timeout di distribuzione dell'input, sono quasi sempre visibili all'utente e molto importanti da mitigare.
Periodo di timeout predefinito: 5 secondi.
Gli errori ANR di distribuzione dell'input sono in genere causati da problemi sul thread principale. Se il thread principale è stato bloccato in attesa di acquisire un blocco, può essere coinvolto anche il thread titolare.
Per evitare errori ANR di distribuzione dell'input, segui queste best practice:
- Non eseguire operazioni di blocco o a lunga esecuzione nel thread principale. Valuta la possibilità di
utilizzare
StrictModeper rilevare attività accidentali sul thread principale. - Ridurre al minimo la contesa di blocchi tra il thread principale e gli altri thread.
- Minimizza il lavoro non UI sul thread principale, ad esempio durante la gestione delle trasmissioni o l'esecuzione dei servizi.
Cause comuni
Di seguito sono riportate alcune cause comuni e le correzioni suggerite per gli errori ANR di distribuzione dell'input.
| Causa | Che cosa accade | Correzioni suggerite |
|---|---|---|
| Chiamata a Binder lenta | Il thread principale effettua una chiamata a Binder sincrona lunga. | Sposta la chiamata dal thread principale o prova a ottimizzarla, se sei il proprietario dell'API. |
| Molte chiamate consecutive al binder | Il thread principale esegue molte chiamate a Binder sincrone consecutive. | Non eseguire chiamate binder in un ciclo stretto. |
| Blocco di I/O | Il thread principale effettua una chiamata di I/O di blocco, ad esempio l'accesso a un database o alla rete. | Sposta tutte le operazioni di I/O bloccanti dal thread principale. |
| Conflitto blocco | Il thread principale è bloccato in attesa di acquisire un blocco. | Ridurre la contesa di blocchi tra il thread principale e altri thread. Ottimizza il codice lento nell'altro thread. |
| Expensive frame | Rendering di troppi contenuti in un singolo frame, con conseguente jank grave. | Ridurre il lavoro di rendering del frame. Non utilizzare gli algoritmi n2. Utilizza componenti efficienti per attività come lo scorrimento o la paginazione, ad esempio la libreria Paging di Jetpack. |
| Bloccata da un altro componente | È in esecuzione un componente diverso, ad esempio un broadcast receiver, che blocca il thread principale. | Sposta il lavoro non UI fuori dal thread principale il più possibile. Esegui i ricevitori di trasmissione su un thread diverso. |
| Blocco della GPU | Il blocco della GPU è un problema di sistema o hardware che causa il blocco del rendering e quindi un errore ANR di distribuzione dell'input. | Purtroppo, in genere non ci sono correzioni sul lato app. Se possibile, contatta il team hardware per risolvere il problema. |
Come eseguire il debug
Inizia il debug esaminando la firma del cluster ANR in Google Play Console o Firebase Crashlytics. Il cluster in genere contiene i frame principali sospettati di causare l'errore ANR.
Il seguente diagramma di flusso mostra come determinare la causa di un ANR di invio del timeout di input.
Play vitals può rilevare ed eseguire il debug di alcune di queste cause comuni di errori ANR. Ad esempio, se le metriche vitals rilevano che si è verificato un errore ANR a causa di un conflitto di blocco, possono riassumere il problema e la correzione consigliata nella sezione Approfondimenti dell'errore ANR.
Nessuna finestra selezionata
Mentre eventi come il tocco vengono inviati direttamente alla finestra pertinente in base al test di hit, eventi come i tasti hanno bisogno di una destinazione. Questo target è chiamato finestra mirata. C'è solo una finestra attiva per display e di solito è la finestra con cui l'utente sta interagendo. Se non viene trovata una finestra attiva, l'input genera un ANR no-focused-window. Un errore ANR senza finestra attiva è un tipo di errore ANR di distribuzione dell'input.
Periodo di timeout predefinito: 5 secondi.
Cause comuni
Gli ANR senza finestra attiva sono in genere causati da uno dei seguenti problemi:
- L'app sta eseguendo troppe operazioni ed è troppo lenta per disegnare il primo frame.
- La finestra principale non è selezionabile. Se una finestra è contrassegnata con
FLAG_NOT_FOCUSABLE, l'utente non può inviare eventi di tasti o pulsanti.
Kotlin
override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.addFlags(WindowManager.LayoutParams.FLAG_FLAG_NOT_FOCUSABLE) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); }
Timeout del broadcast receiver
Un errore ANR del broadcast receiver si verifica quando un broadcast receiver non gestisce una
trasmissione in tempo. Per i ricevitori sincroni o quelli che non chiamano
goAsync(), un timeout significa che onReceive() non è stato completato in
tempo. Per i ricevitori asincroni o i ricevitori che chiamano goAsync(), un timeout significa
che PendingResult.finish() non è stato chiamato in tempo.
Gli ANR del broadcast receiver si verificano spesso in questi thread:
- Thread principale, se il problema è l'avvio lento dell'app.
- Thread che esegue il broadcast receiver, se il problema è il codice
onReceive()lento. - Thread di lavoro di trasmissione, se il problema è il codice di trasmissione
goAsync()lento.
Per evitare errori ANR del broadcast receiver, segui queste best practice:
- Assicurati che l'avvio dell'app sia rapido, poiché viene conteggiato nel timeout ANR se l'app viene avviata per gestire la trasmissione.
- Se viene utilizzato
goAsync(), assicurati chePendingResult.finish()venga chiamato rapidamente. È soggetto allo stesso timeout ANR dei ricevitori di trasmissione sincroni. - Se viene utilizzato
goAsync(), assicurati che i thread di lavoro non siano condivisi con altre operazioni di blocco o a esecuzione prolungata. - Valuta la possibilità di utilizzare
registerReceiver()per eseguire i ricevitori di trasmissione in un thread non principale, per evitare di bloccare il codice dell'interfaccia utente in esecuzione nel thread principale.
Periodi di timeout
I periodi di timeout di ricezione della trasmissione dipendono dal fatto che il flag di intent in primo piano sia impostato e dalla versione della piattaforma.
| Tipo di intent | Android 13 e versioni precedenti | Android 14 e versioni successive |
|---|---|---|
Intent di priorità in primo piano ( |
10 secondi |
10-20 secondi, a seconda che il processo sia affamato di CPU |
Intent di priorità in background ( |
60 secondi |
60-120 secondi, a seconda che il processo sia affamato di CPU |
Per verificare se il flag FLAG_RECEIVER_FOREGROUND è impostato, cerca "flg=" nell'oggetto
dell'ANR e controlla la presenza di 0x10000000. Se questo bit è impostato, l'intent ha FLAG_RECEIVER_FOREGROUND impostato e quindi il timeout è più breve.
Esempio di oggetto ANR con timeout di trasmissione breve (10-20 secondi):
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Esempio di oggetto ANR con timeout di trasmissione lungo (60-120 secondi):
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
Come vengono misurati gli orari di trasmissione
La misurazione della durata della trasmissione inizia quando la trasmissione viene inviata da
system_server all'app e termina quando l'app termina l'elaborazione della
trasmissione. Se il processo dell'app non era già in esecuzione, deve anche eseguire un avvio
a freddo entro il periodo di timeout ANR. Pertanto, l'avvio lento dell'app può causare
errori ANR del broadcast receiver.
La figura seguente mostra che la sequenza temporale ANR del broadcast receiver è allineata a determinati processi dell'app.
La misurazione del timeout ANR termina quando il ricevitore completa l'elaborazione della trasmissione: il momento esatto in cui ciò avviene dipende dal fatto che si tratti di un ricevitore sincrono o asincrono.
- Per i destinatari sincroni, la misurazione si interrompe quando viene restituito
onReceive(). - Per i ricevitori asincroni, la misurazione si interrompe quando viene chiamato
PendingResult.finish().
Cause comuni
Di seguito sono riportate alcune cause comuni e le correzioni suggerite per gli errori ANR del broadcast receiver.
| Causa | Applicabile a | Che cosa è successo | Correzione suggerita |
|---|---|---|---|
| Avvio dell'app lento | Tutti i ricevitori | L'app ha impiegato troppo tempo per l'avvio a freddo. | Ottimizza l'avvio lento dell'app. |
onReceive() non programmato | Tutti i ricevitori | Il thread del broadcast receiver era occupato a svolgere altre attività e non è stato possibile
avviare il metodo onReceive(). | Non eseguire attività a lunga esecuzione sul thread del ricevitore (o sposta il ricevitore su un thread dedicato). |
Dispositivo onReceive() lento | Tutti i ricevitori, ma principalmente quelli sincroni | Il metodo onReceive() è stato avviato, ma
è stato bloccato o rallentato, quindi non è stato completato in tempo. | Ottimizza il codice del ricevitore lento. |
| Attività del ricevitore asincrone non pianificate | goAsync()
ricevitori | Il metodo onReceive() ha tentato di eseguire il lavoro
su un pool di thread di lavoro bloccato, quindi il lavoro non è mai iniziato. |
Ottimizza le chiamate lente o bloccanti oppure utilizza thread diversi per i worker di trasmissione rispetto ad altre attività a lunga esecuzione. |
| Worker lenti o bloccati | goAsync() ricevitori |
Si è verificata un'operazione di blocco o lenta in un punto del pool di thread di lavoro
durante l'elaborazione della trasmissione. Pertanto, PendingResult.finish
non è stato chiamato in tempo. | Ottimizza il codice del ricevitore async lento. |
Ho dimenticato di chiamare PendingResult.finish |
goAsync() ricevitori |
La chiamata a finish() non è presente nel percorso del codice. |
Assicurati che finish() venga sempre chiamato. |
Come eseguire il debug
In base alla firma del cluster e al report ANR, puoi individuare il thread su cui viene eseguito il ricevitore e quindi il codice specifico mancante o in esecuzione lentamente.
Il seguente diagramma di flusso mostra come determinare la causa di un errore ANR del ricevitore di trasmissione.
Trovare il codice del ricevitore
Google Play Console mostra la classe del ricevitore e l'intent di trasmissione nella firma ANR. Cerca quanto segue:
cmp=<receiver class>act=<broadcast_intent>
Ecco un esempio di firma ANR di un broadcast receiver:
com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }
Trova il thread che esegue il metodo onReceive()
Se utilizzi Context.registerReceiver per specificare un gestore personalizzato,
è il thread che esegue questo gestore. In caso contrario, si tratta del thread principale.
Esempio: attività del ricevitore asincrone non pianificate
Questa sezione illustra un esempio di come eseguire il debug di un errore ANR di un broadcast receiver.
Supponiamo che la firma ANR sia simile alla seguente:
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
In base alla firma, sembra che l'intent di trasmissione sia
android.accounts.LOG_ACCOUNTS_CHANGED e la classe del ricevitore sia
com.example.app.MyReceiver.
Dal codice del ricevitore, puoi determinare che il pool di thread "BG Thread
[0,1,2,3]" esegue il lavoro principale per elaborare questa trasmissione. Se esamini i dump
dello stack, puoi notare che tutti e quattro i thread in background (BG) hanno lo stesso pattern:
eseguono una chiamata di blocco, getDataSync. Poiché tutti i thread BG erano occupati, la
trasmissione non è stata elaborata in tempo, il che ha portato a un errore ANR.
BG Thread #0 (tid=26) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture:563)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture:68)
at com.example.app.getDataSync(<MyClass>:152)
...
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at com.google.android.libraries.concurrent.AndroidExecutorsModule.lambda$withStrictMode$5(AndroidExecutorsModule:451)
at com.google.android.libraries.concurrent.AndroidExecutorsModule$$ExternalSyntheticLambda8.run(AndroidExecutorsModule:1)
at java.lang.Thread.run(Thread.java:1012)
at com.google.android.libraries.concurrent.ManagedPriorityThread.run(ManagedPriorityThread:34)
There are several approaches to fix the issue:
- Find out why
getDataSyncis slow and optimize. - Don't run
getDataSyncon all four BG threads. - More generally, ensure that the BG thread pool isn't saturated with long-running operations.
- Use a dedicated thread pool for
goAsyncworker tasks. - Use an unbounded thread pool instead of the bounded BG thread pool
Example: slow app startup
A slow app startup can cause several types of ANRs, especially broadcast
receiver and execute service ANRs. The cause of an
ANR is likely slow app startup if you see ActivityThread.handleBindApplication
in the main thread stacks.
Execute service timeout
An execute service ANR happens when the app's main thread doesn't start a
service in time. Specifically, a service doesn't finish executing
onCreate() and onStartCommand() or onBind() within the
timeout period.
Default timeout period: 20 seconds for foreground service; 200 seconds for
background service. The ANR timeout period includes the app cold start, if
necessary, and calls to onCreate(), onBind(), or onStartCommand().
To avoid execute service ANRs, follow these general best practices:
- Make sure that app startup is fast, since it's counted in the ANR timeout if the app is started to run the service component.
- Make sure that the service's
onCreate(),onStartCommand(), andonBind()methods are fast. - Avoid running any slow or blocking operations on the main thread from other components; these operations can prevent a service from starting quickly.
Common causes
The following table lists common causes of execute service ANRs and suggested fixes.
| Cause | What | Suggested fix |
|---|---|---|
| Slow app startup | The app takes too long to perform a cold start. | Optimize slow app start. |
Slow onCreate(), onStartCommand(), or
onBind() |
The service component's onCreate(),
onStartCommand(), or onBind() method takes too long to
execute on the main thread. |
Optimize slow code. Move slow operations off the critical path where possible. |
Not scheduled (main thread blocked before onStart()) |
The app's main thread is blocked by another component before the service can be started. | Move other component's work off the main thread. Optimize other component's blocking code. |
How to debug
From the cluster signature and ANR report in Google Play Console or Firebase Crashlytics, you can often determine the cause of the ANR based on what the main thread is doing.
The following flow chart describes how to debug an execute service ANR.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
Find the service component class in the ANR signature. In Google Play Console, the service component class is shown in the ANR signature. In the following example ANR details, it's
com.example.app/MyService.com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly Executing service com.example.app/com.example.app.MyServiceDetermine whether the slow or block operation is part of app startup, the service component, or elsewhere by checking for the following important function call(s) in the main threads.
Function call(s) in main thread stacks What it means android.app.ActivityThread.handleBindApplicationApp was starting up, so the ANR was caused by slow app start. <ServiceClass>.onCreate()
[...]
android.app.ActivityThread.handleCreateService
Service was being created, so the ANR was likely caused by slow onCreate()code.<ServiceClass>.onBind()
[...]
android.app.ActivityThread.handleBindService
Service was being bound, so the ANR was likely caused by slow onBind()code.<ServiceClass>.onStartCommand()
[...]
android.app.ActivityThread.handleServiceArgs
Service was being started, so the ANR was likely caused by slow onStartCommand()code.For example, if the
onStartCommand()method in theMyServiceclass is slow, the main threads will look like this:at com.example.app.MyService.onStartCommand(FooService.java:25) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4820) at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(unavailable:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2289) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:205) at android.os.Looper.loop(Looper.java:294) at android.app.ActivityThread.main(ActivityThread.java:8176) at java.lang.reflect.Method.invoke(Native method:0)Se non riesci a visualizzare nessuna delle chiamate di funzioni importanti, ci sono altre due possibilità:
- Il servizio è in esecuzione o in fase di arresto, il che significa che gli stack vengono acquisiti troppo tardi. In questo caso, puoi ignorare l'ANR come falso positivo.
- È in esecuzione un componente dell'app diverso, ad esempio un ricevitore di trasmissione. In questo caso, il thread principale è probabilmente bloccato in questo componente, impedendo l'avvio del servizio.
Se vedi una chiamata di funzione chiave e riesci a determinare dove si verifica l'errore ANR in generale, controlla il resto degli stack del thread principale per trovare l'operazione lenta e ottimizzarla o spostarla dal percorso critico.
- Assicurati che l'avvio dell'app sia rapido, poiché viene conteggiato nel timeout ANR se l'app viene avviata per eseguire il content provider.
- Assicurati che le query del fornitore di contenuti siano veloci.
- Non eseguire molte chiamate simultanee al binder di blocco che possono bloccare tutti i thread del binder dell'app.
- Blocchi silenziosi dell'interfaccia utente: l'interfaccia utente dell'app smette di rispondere all'input dell'utente.
- Arresti anomali: l'app potrebbe arrestarsi in modo anomalo con un
illegalStateException. - ANR: il sistema potrebbe attivare un timeout ANR.
- Scheduling Traversals:
ViewRootImplpianifica un attraversamento, ovvero un passaggio di layout e disegno, pubblicando una barriera di sincronizzazione nellaMessageQueuedel thread UI. Questa barriera mette in pausa l'elaborazione normale dei messaggi in modo che il layout dell'interfaccia utente possa avere la priorità. - Condizione di competizione: quando più thread tentano di invalidare una visualizzazione contemporaneamente, competono per pianificare questo attraversamento. Entrambi i thread potrebbero inserire correttamente una barriera di sincronizzazione, ma il framework memorizza il token solo per uno di essi.
- Blocco della UI: quando viene eseguito l'attraversamento, viene rimossa solo la singola barriera salvata. Le barriere secondarie "trapelate" rimangono in coda a tempo indeterminato, bloccando definitivamente il thread dell'interfaccia utente dall'elaborazione di qualsiasi messaggio sincrono.
- Interagisci solo con gli oggetti
View, inclusa la lettura di proprietà comewidthoheighto l'impostazione di proprietà comeTextView's text, nel thread in cui hai creato la gerarchia di oggetti View. Quasi sempre è il thread principale o dell'interfaccia utente. - Se non puoi garantire di eseguire l'operazione sul thread dell'interfaccia utente quando accedi alle visualizzazioni, supponi che non sia sicuro farlo. Ad esempio, se utilizzi
Coroutinesper il lavoro in background, devi passare al dispatcher principale prima di manipolare gli oggettiView. ContextCompat#getMainExecutor(android.content.Context)View.post(Runnable)Activity.runOnUiThread(Runnable)- Installa una versione abilitata al debug della tua app su un dispositivo con Android 17 o versioni successive.
- Apri l'app Impostazioni del dispositivo e vai a Sistema > Avanzate > Opzioni sviluppatore > Modifiche alla compatibilità delle app.
- Seleziona l'app dall'elenco.
- Nell'elenco delle modifiche, trova e attiva l'opzione
ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS. - Telemetria e monitoraggio: questo listener consente alla tua app di ricevere callback quando viene richiamata un'API View da un thread errato, semplificando la registrazione della telemetria e l'individuazione dei bug.
- Esecuzione in linea: il listener viene chiamato in linea, consentendoti di acquisire e ispezionare l'analisi dello stack esatta nel momento in cui si verifica la violazione.
- Problema a livello di sistema. Il processo non è stato pianificato a causa del carico elevato del sistema o di un problema nel server di sistema.
- Dump dello stack in ritardo. Il thread recuperato nel breve periodo tra l'attivazione di ANR e il dump degli stack. La latenza nei Pixel su Android 13 è di circa 100 ms, ma può superare 1 s. La latenza nei Pixel con Android 14 è generalmente inferiore a 10 ms.
- Attribuzione errata del thread. Il thread utilizzato per creare la firma ANR non era il thread non rispondente effettivo che ha causato l'ANR.
- Visualizza violazione del threading. Se la tua app modifica una visualizzazione su un thread in background, può attivare una race condition negli elementi interni della visualizzazione che impedisce l'esecuzione delle attività del thread dell'interfaccia utente
- Sovraccarico di sistema:valuta la pressione complessiva delle risorse del dispositivo, ad esempio la carenza di CPU, memoria o I/O a livello di sistema come causa principale della mancanza di risposta.
- Attribuzione errata dei thread:controlla i thread di binder e worker per rilevare deadlock,
contesa di blocchi che influisce sul thread principale o thread in background bloccati
che elaborano componenti asincroni (ad es.
goAsync()). - Visualizza violazioni del threading:esegui la scansione dei thread in background per rilevare modifiche illegali alla gerarchia di visualizzazione dell'interfaccia utente, che possono orfanizzare una barriera di sincronizzazione in MessageQueue e bloccare definitivamente tutti i messaggi sincroni.
- L'acquisizione dello stack richiede troppo tempo e si verifica un timeout.
- Il processo è terminato o è stato interrotto prima che venissero acquisite le pile.
Per saperne di più sui servizi, consulta le pagine seguenti:
Il fornitore di contenuti non risponde
Un errore ANR del fornitore di contenuti si verifica quando un fornitore di contenuti remoto impiega più tempo del periodo di timeout per rispondere a una query e viene interrotto.
Periodo di timeout predefinito: specificato dal fornitore di contenuti utilizzando
ContentProviderClient.setDetectNotResponding. Il periodo di timeout ANR
include il tempo totale di esecuzione di una query del fornitore di contenuti remoto, che
include l'avvio a freddo dell'app remota se non era già in esecuzione.
Per evitare gli errori ANR dei fornitori di contenuti, segui queste best practice:
Cause comuni
La tabella seguente elenca le cause comuni degli errori ANR del fornitore di contenuti e le correzioni suggerite.
| Causa | Che cosa accade | Indicatore | Correzione suggerita |
|---|---|---|---|
| Query del fornitore di contenuti lenta | Il fornitore di contenuti impiega troppo tempo per l'esecuzione o è bloccato. | Il frame android.content.ContentProvider$Transport.query
si trova nel thread del raccoglitore. |
Ottimizza la query del fornitore di contenuti. Scopri cosa blocca il thread del binder. |
| Avvio dell'app lento | L'app del fornitore di contenuti impiega troppo tempo per avviarsi. | Il frame ActivityThread.handleBindApplication si trova nel
thread principale. |
Ottimizza l'avvio dell'app. |
| Esaurimento dei thread del binder: tutti i thread del binder sono occupati | Tutti i thread del binder sono occupati a gestire altre richieste sincrone, quindi la chiamata del binder del content provider non può essere eseguita. | L'app non si avvia, tutti i thread del binder sono occupati e il provider di contenuti non è in esecuzione. | Ridurre il carico sui thread del raccoglitore. ovvero, effettuare meno chiamate binder sincrone in uscita o svolgere meno lavoro durante la gestione delle chiamate in entrata. |
Come eseguire il debug
Per eseguire il debug di un errore ANR del content provider utilizzando la firma del cluster e il report ANR in Google Play Console o Firebase Crashlytics, esamina le attività del thread principale e dei thread binder.
Il seguente diagramma di flusso descrive come eseguire il debug di un errore ANR del content provider:
Il seguente snippet di codice mostra l'aspetto del thread del binder quando è bloccato a causa di una query lenta del content provider. In questo caso, la query del fornitore di contenuti è in attesa di blocco all'apertura di un database.
binder:11300_2 (tid=13) Blocked
Waiting for osm (0x01ab5df9) held by at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers:182)
at com.example.app.MyClass.blockingGetOpenDatabase(FooClass:171)
[...]
at com.example.app.MyContentProvider.query(MyContentProvider.java:915)
at android.content.ContentProvider$Transport.query(ContentProvider.java:292)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:107)
at android.os.Binder.execTransactInternal(Binder.java:1339)
at android.os.Binder.execTransact(Binder.java:1275)
Il seguente snippet di codice mostra l'aspetto del thread principale quando è bloccato a causa dell'avvio lento dell'app. In questo caso, l'avvio dell'app è lento a causa della contesa di blocchi durante l'inizializzazione di Dagger.
main (tid=1) Blocked
[...]
at dagger.internal.DoubleCheck.get(DoubleCheck:51)
- locked 0x0e33cd2c (a qsn)at dagger.internal.SetFactory.get(SetFactory:126)
at com.myapp.Bar_Factory.get(Bar_Factory:38)
[...]
at com.example.app.MyApplication.onCreate(DocsApplication:203)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2235)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8170)
at java.lang.reflect.Method.invoke(Native method:0)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Risposta lenta al lavoro
Un errore ANR di risposta lenta del job si verifica quando l'app impiega troppo tempo per rispondere a
JobService.onStartJob() o JobService.onStopJob() oppure impiega troppo tempo per
fornire una notifica utilizzando JobService.setNotification(). Ciò suggerisce che
il thread principale dell'app è bloccato in un'altra attività.
Se il problema riguarda JobService.onStartJob() o JobService.onStopJob(),
controlla cosa sta succedendo nel thread principale. Se il problema riguarda
JobService.setNotification(), assicurati di chiamare il prima possibile.
Non svolgere molto lavoro prima di fornire la notifica.
Visualizza violazione del threading
Se la tua app modifica una visualizzazione su un thread in background, può attivare una condizione di competizione nello stato interno della gerarchia di visualizzazione. Questa violazione può impedire al thread principale (UI) di eseguire messaggi sincroni, causando problemi di stabilità critici:
Questa violazione del threading è una delle cause principali delle analisi dello stack ANR in cui il thread principale appare inattivo (ad es. acquisito in "nativePollOnce" o "main thread idle").
Perdita della barriera di sincronizzazione
Le visualizzazioni possono essere invalidate indirettamente modificandone lo stato (ad esempio,
chiamando TextView.setText, View.setVisibility) o direttamente chiamando
View.invalidate o View.requestLayout. Quando una visualizzazione viene invalidata,
ViewRootImpl pianifica un attraversamento per eseguire la misurazione, il layout e il disegno
per aggiornare lo stato dell'interfaccia utente.
Di conseguenza, l'interfaccia utente si blocca. Poiché MessageQueue non può elaborare alcun
messaggio sincrono oltre le barriere di perdita, il thread principale entra in uno stato di inattività. Questo problema in genere si manifesta come ANR con nativePollOnce nell'analisi dello stack.
Per evitare violazioni del threading delle visualizzazioni, segui queste best practice:
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { val data = myRepository.getData() withContext(Dispatchers.Main) { // Switch context to Main myTextView.text = data.title } }
Per aiutarti a seguire le best practice, Android offre diversi modi per accedere al thread UI da altri thread:
I caricatori di immagini più diffusi, i framework di estensioni reattive, le librerie di bus di eventi e altre soluzioni di threading in genere offrono modi per osservare determinati eventi sul thread principale. È utile per gli osservatori e i listener di eventi che devono ottenere e impostare lo stato delle visualizzazioni.
Come eseguire il debug
Android 17 introduce nuovi strumenti per aiutarti a identificare, tracciare e risolvere le violazioni del threading delle visualizzazioni durante lo sviluppo.
1. Strumenti del framework di compatibilità
Gli strumenti del framework di compatibilità consentono agli sviluppatori di app di attivare e disattivare singolarmente le modifiche al comportamento utilizzando le opzioni sviluppatore o ADB. Puoi attivare/disattivare le modifiche al comportamento delle violazioni del threading della visualizzazione seguendo questi passaggi:
Puoi anche attivare o disattivare il flag utilizzando ADB:
$ adb shell am compat enable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
$ adb shell am compat disable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
L'attivazione di questo flag forza l'app a generare un'eccezione e a bloccarsi ogni volta che viene chiamata un'API View dal thread errato, aiutandoti a rilevare e correggere i problemi di violazione del threading della visualizzazione.
2. API CalledFromWrongThreadListener
Puoi anche implementare l'API
View#registerCalledFromWrongThreadListener globale per rilevare l'accesso
improprio ai thread in modo programmatico.
android {
//...
compileSdk = 37
}
val listener = object : View.CalledFromWrongThreadListener { override fun onCalledFromWrongThread() { // Handle the issue, e.g. crash if this is a dev build, or log an event // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") // Unregister the listener to avoid redundant notifications for the same issue View.unregisterCalledFromWrongThreadListener(this) } } View.registerCalledFromWrongThreadListener(listener)
nativePollOnce
Se negli stack ANR vedi il frame "nativePollOnce" o "message queue idle", spesso indica che il thread sospettato di non rispondere era in realtà inattivo e in attesa di messaggi del looper. In Google Play Console, i dettagli degli errori ANR sono visualizzati nel seguente modo:
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
Ad esempio, se il thread principale è inattivo, gli stack hanno il seguente aspetto:
"main" tid=1 NativeMain threadIdle
#00 pc 0x00000000000d8b38 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
#01 pc 0x0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
#02 pc 0x0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#03 pc 0x000000000011409c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (Native method)
at android.os.MessageQueue.next (MessageQueue.java:339) at android.os.Looper.loop (Looper.java:208)
at android.app.ActivityThread.main (ActivityThread.java:8192)
at java.lang.reflect.Method.invoke (Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:626)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1015)
Esistono diversi motivi per cui il thread sospettato di non rispondere può essere inattivo:
Poiché i trigger sottostanti di un errore ANR "nativePollOnce" variano in base al tipo di ANR, la diagnosi della categoria ANR specifica può aiutare a identificare i passaggi pratici per risolvere il problema all'interno dell'app.
| Categoria | Causa | Correzione suggerita |
|---|---|---|
| Input timeout di invio | Problema a livello di sistema Dump dello stack in ritardo |
Non occorre alcun intervento. |
| Nessuna finestra selezionata | Problema a livello di sistema Dump dello stack in ritardo Visualizza violazione del threading |
Controlla la base di codice per rilevare violazioni del threading della visualizzazione. |
| Timeout del broadcast receiver | Dump della pila in ritardo Problema a livello di sistema Attribuzione errata del thread Visualizza violazione del threading |
Ispeziona i thread pertinenti nel dump dello stack. Controlla la base di codice per rilevare violazioni del threading della visualizzazione. |
| Timeout di esecuzione del servizio | Dump dello stack in ritardo Problema a livello di sistema Visualizza violazione del threading |
Controlla la base di codice per rilevare violazioni del threading della visualizzazione. |
| Il fornitore di contenuti non risponde | Dump dello stack in ritardo Problema a livello di sistema Attribuzione errata del thread |
Ispeziona i thread pertinenti nel dump dello stack. |
Di seguito sono riportati i passaggi consigliati per analizzare gli errori ANR nativePollOnce.
Nessuno stack frame
Alcuni report ANR non includono gli stack con l'errore ANR, il che significa che il dump dello stack non è riuscito durante la generazione del report ANR. Esistono un paio di possibili motivi per la mancanza di frame dello stack:
[...]
--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1666030897753
window_ms: 300000
libdebuggerd_client: failed to read status response from tombstoned: timeout reached?
----- Waiting Channels: pid 7068 at 2022-10-18 02:21:37.<US_SOCIAL_SECURITY_NUMBER>+0800 -----
[...]
Gli errori ANR senza frame dello stack non sono azionabili dalla firma del cluster o dal report ANR. Per eseguire il debug, esamina altri cluster per l'app, poiché se un problema è abbastanza grande di solito ha un proprio cluster in cui sono presenti i frame dello stack. Un'altra opzione è esaminare le tracce Perfetto.
Problemi noti
Mantenere un timer nel processo dell'app per completare la gestione della trasmissione prima che venga attivato un errore ANR potrebbe non funzionare correttamente a causa del modo asincrono in cui il sistema monitora gli errori ANR.