Diagnostica e correggi gli errori ANR

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_server per 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 StrictMode per 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.

Figura 1. Come eseguire il debug di un errore ANR di distribuzione dell'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.

Figura 2. Rilevamento degli errori ANR di Play vitals.

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 che PendingResult.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

(FLAG_RECEIVER_FOREGROUND set)

10 secondi

10-20 secondi, a seconda che il processo sia affamato di CPU

Intent di priorità in background

(FLAG_RECEIVER_FOREGROUND non impostato)

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.

Figura 3. La sequenza temporale ANR del broadcast receiver.

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().
Figura 4. Endpoint di misurazione del timeout ANR per ricevitori sincroni e asincroni.

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.

Figura 5. Come eseguire il debug di un errore ANR di un broadcast receiver.

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 getDataSync is slow and optimize.
  • Don't run getDataSync on 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 goAsync worker 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(), and onBind() 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.

Figure 6. 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:

  1. 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.MyService
    
  2. Determine 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.handleBindApplication App 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 the MyService class 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.
  3. 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.

  4. 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:

    • 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.

    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:

    Figura 7. Come eseguire il debug di un errore ANR del fornitore di contenuti.

    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

    Prova Compose
    Jetpack Compose è il toolkit per la UI consigliato per Android.

    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:

    • 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.

    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.

    1. Scheduling Traversals: ViewRootImpl pianifica un attraversamento, ovvero un passaggio di layout e disegno, pubblicando una barriera di sincronizzazione nella MessageQueue del thread UI. Questa barriera mette in pausa l'elaborazione normale dei messaggi in modo che il layout dell'interfaccia utente possa avere la priorità.
    2. 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.
    3. 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.

    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:

    • Interagisci solo con gli oggetti View, inclusa la lettura di proprietà come width o height o l'impostazione di proprietà come TextView'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 Coroutines per il lavoro in background, devi passare al dispatcher principale prima di manipolare gli oggetti View.

    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:

    1. Installa una versione abilitata al debug della tua app su un dispositivo con Android 17 o versioni successive.
    2. Apri l'app Impostazioni del dispositivo e vai a Sistema > Avanzate > Opzioni sviluppatore > Modifiche alla compatibilità delle app.
    3. Seleziona l'app dall'elenco.
    4. Nell'elenco delle modifiche, trova e attiva l'opzione ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS.

    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.

    • 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.
    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:

    • 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

    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.

    1. 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.
    2. 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()).
    3. 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.

    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:

    • L'acquisizione dello stack richiede troppo tempo e si verifica un timeout.
    • Il processo è terminato o è stato interrotto prima che venissero acquisite le pile.
    [...]
    
    --- 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.