Mantieni reattiva la tua app

Figura 1. Una finestra di dialogo ANR visualizzata all'utente.

Questo documento descrive in che modo il sistema Android determina se un'app non risponde e mostra come mantenerla reattiva.

Indipendentemente dalla qualità del codice, è possibile che l'app risulti lenta, si blocchi, si blocchi per periodi di tempo significativi o impieghi troppo tempo per elaborare l'input. Se l'app è in primo piano e non risponde, l'utente visualizza una finestra di dialogo L'applicazione non risponde (ANR), come mostrato nella Figura 1. La finestra di dialogo ANR consente all'utente di forzare l'uscita dall'app. Se l'app non è in primo piano, viene arrestata silenziosamente. È fondamentale progettare la reattività dell'app per ridurre al minimo le finestre di dialogo ANR.

Trigger ANR

In genere, il sistema visualizza un errore ANR se un'app non risponde all'input dell'utente sul thread principale, noto anche come thread dell'interfaccia utente, impedendo al sistema di elaborare gli eventi di input dell'utente in entrata.

Ad esempio, un ANR può verificarsi se un'app esegue un'operazione di I/O di blocco, come l'accesso alla rete, sul thread dell'interfaccia utente. Un altro esempio è quando un'app impiega troppo tempo per creare una struttura in memoria elaborata o per calcolare la mossa successiva in un gioco sul thread dell'interfaccia utente.

In Android, la reattività delle app viene monitorata dai servizi di sistema ActivityManager e WindowManager. Android visualizza la finestra di dialogo ANR per un'app quando rileva una delle seguenti condizioni:

  • Nessuna risposta a un evento di input, ad esempio eventi di pressione dei tasti o tocco dello schermo, entro 5 secondi.
  • Un BroadcastReceiver non termina l'esecuzione entro 10-20 secondi per gli intent in primo piano. Per ulteriori informazioni, consulta Timeout del broadcast receiver.

Evitare gli errori ANR

Di seguito sono riportati alcuni suggerimenti generali per evitare gli errori ANR. Per maggiori dettagli sulla diagnosi e sul debug di diversi tipi di errori ANR, consulta le altre pagine di questa sezione.

  • Mantieni il thread principale sbloccato in ogni momento e utilizza i thread in modo strategico.

    • Non eseguire operazioni di blocco o di lunga durata sul thread principale dell'app. Utilizza invece le coroutine Kotlin per trasferire il lavoro nei dispatcher in background (ad esempio Dispatchers.IO o Dispatchers.Default). Utilizza meccanismi come viewModelScope per avviare in sicurezza queste attività in background o LaunchedEffect per attivarle in risposta alle modifiche dello stato di Compose.

    • Cerca di ridurre al minimo qualsiasi contesa di blocco tra il thread principale e gli altri thread.

    • Riduci al minimo qualsiasi lavoro non correlato all'UI sul thread principale, ad esempio quando gestisci le trasmissioni o esegui i servizi. Qualsiasi metodo o funzione eseguita nel thread dell'UI deve svolgere il minor lavoro possibile. In particolare, le attività devono fare il meno possibile per la configurazione nei metodi del ciclo di vita chiave, come onCreate e onResume. Non eseguire mai calcoli di I/O o di blocco pesanti direttamente all'interno di una funzione componibile. In questo modo, il thread dell'UI viene bloccato durante la composizione e la ricomposizione. Per ulteriori informazioni sulle soluzioni disponibili per la pianificazione del lavoro su un thread in background e la comunicazione con l'UI, consulta la panoramica delle attività in background.

    • Fai attenzione quando condividi i pool di thread tra i componenti. Non utilizzare gli stessi thread per operazioni di blocco potenzialmente lunghe e attività sensibili al tempo, come la ricezione di trasmissioni.

  • Mantieni l'avvio dell'app veloce. Riduci al minimo le operazioni lente o di blocco nel codice di avvio dell'app, ad esempio i metodi eseguiti durante la configurazione dell'iniezione delle dipendenze (come con Hilt) o i componenti inizializzati utilizzando la libreria Jetpack App Startup. Puoi ottimizzare ulteriormente l'avvio dell'app utilizzando i profili di baseline , i profili di avvio e R8.

  • Se utilizzi BroadcastReceiver, valuta la possibilità di eseguire i ricevitori di broadcast in un thread non principale utilizzando Context.registerReceiver. Per ulteriori informazioni, consulta Errori ANR in BroadcastReceiver.

Errori ANR in BroadcastReceiver

BroadcastReceiver il tempo di esecuzione è limitato perché i ricevitori di broadcast sono progettati per eseguire piccole quantità di lavoro discrete in background, ad esempio salvare un'impostazione o registrare un Notification. Pertanto, come per altri metodi chiamati nel thread dell'interfaccia utente, le app devono evitare operazioni o calcoli potenzialmente di lunga durata in un broadcast receiver. Anziché eseguire attività di lunga durata tramite il thread dell'interfaccia utente, eseguile in background per l'esecuzione successiva. Per ulteriori informazioni sulle possibili soluzioni, consulta la panoramica delle attività in background.

Un altro problema comune con gli oggetti BroadcastReceiver si verifica quando vengono eseguiti troppo spesso. L'esecuzione frequente in background può ridurre la quantità di memoria disponibile per altre app. Per ulteriori informazioni su come attivare e disattivare BroadcastReceiver in modo efficiente, consulta la panoramica delle trasmissioni.

Rafforzare la reattività

In genere, 100-200 ms è la soglia oltre la quale gli utenti percepiscono la lentezza di un'app. Ecco alcuni suggerimenti aggiuntivi per far sembrare la tua app reattiva agli utenti:

  • Se l'app esegue attività in background in risposta all'input dell'utente, mostra che i progressi sono in corso, ad esempio con un CircularProgressIndicator o LinearProgressIndicator nell'UI.

  • Per i giochi in particolare, esegui i calcoli per le mosse in una coroutine in background o in un thread di lavoro.

  • Se l'app ha una fase di configurazione iniziale che richiede molto tempo, valuta la possibilità di mostrare una schermata iniziale o di eseguire il rendering della composizione iniziale il più rapidamente possibile. Indica che il caricamento è in corso e compila lo stato dell'UI in modo asincrono. In entrambi i casi, ti consigliamo di indicare in qualche modo che i progressi sono in corso, in modo che l'utente non percepisca che l'app è bloccata.

  • Utilizza strumenti per il rendimento come Perfetto e CPU Profiler per determinare i colli di bottiglia nella reattività dell'app.