ANR

Quando il thread dell'interfaccia utente di un'app per Android viene bloccato per troppo tempo, viene attivato un errore "L'applicazione non risponde" (ANR). Se l'app è in primo piano, il sistema mostra una finestra di dialogo all'utente, come mostrato nella figura 1. La finestra di dialogo ANR offre all'utente la possibilità di forzare l'uscita dall'app.

Finestra di dialogo ANR mostrata all'utente.
Figura 1. Finestra di dialogo ANR mostrata all'utente

Gli errori ANR sono un problema perché il thread principale dell'app, responsabile dell'aggiornamento della UI, non può elaborare gli eventi di input dell'utente o disegnare, causando frustrazione nell'utente. Per saperne di più sul thread principale dell'app, consulta la panoramica di processi e thread.

Un errore ANR viene attivato per la tua app quando si verifica una delle seguenti condizioni:

  • Timeout dell'invio dell'input:se l'app non ha risposto a un evento di input (ad esempio la pressione di un tasto o il tocco dello schermo) entro 5 secondi.
  • Esecuzione del servizio:se un servizio dichiarato dalla tua app non riesce a completare l'esecuzione di Service.onCreate e Service.onStartCommand/Service.onBind entro pochi secondi.
  • Service.startForeground non chiamato:se la tua app utilizza Context.startForegroundService per avviare un nuovo servizio in primo piano, ma il servizio non chiama startForeground entro 5 secondi.
  • Trasmissione dell'intent: se un BroadcastReceiver non ha terminato l'esecuzione entro un determinato periodo di tempo. Se l'app ha attività in primo piano, questo timeout è di 5 secondi.
  • Interazioni con JobScheduler:se un JobService non restituisce un risultato da JobService.onStartJob o JobService.onStopJob entro pochi secondi o se un job avviato dall'utente viene avviato e la tua app non chiama JobService.setNotification entro pochi secondi dalla chiamata di JobService.onStartJob. Per le app che hanno come target Android 13 e versioni precedenti, gli ANR sono silenziosi e non vengono segnalati all'app. Per le app che hanno come target Android 14 e versioni successive, gli ANR sono espliciti e vengono segnalati all'app.

Se la tua app presenta errori ANR, puoi utilizzare le indicazioni contenute in questo documento per diagnosticare e risolvere il problema.

Diagnostica gli ANR

Esistono alcuni pattern comuni da cercare quando si diagnosticano gli ANR:

  • L'app esegue operazioni lente che coinvolgono l'I/O sul thread principale.
  • L'app sta eseguendo un calcolo lungo sul thread principale.
  • Il thread principale sta eseguendo una chiamata a Binder sincrona a un altro processo e quest'ultimo impiega molto tempo per restituire il risultato.
  • Il thread principale è bloccato in attesa di un blocco sincronizzato per un'operazione lunga che si sta verificando su un altro thread.
  • Il thread principale è in un deadlock con un altro thread, nel tuo processo o tramite una chiamata binder. Il thread principale non è solo in attesa del completamento di un'operazione lunga, ma si trova in una situazione di deadlock.

Le seguenti tecniche possono aiutarti a determinare la causa degli ANR.

HealthStats

HealthStats fornisce metriche sull'integrità di un'applicazione acquisendo il tempo totale dell'utente e del sistema, il tempo di CPU, la rete, le statistiche radio, il tempo di attivazione/disattivazione dello schermo e le sveglie. In questo modo puoi misurare l'utilizzo complessivo della CPU e il consumo della batteria.

Debug

Debug ti aiuta a ispezionare le app per Android durante lo sviluppo, inclusi i conteggi di tracciamento e allocazione per identificare jank e ritardi nelle app. Puoi anche utilizzare Debug per ottenere contatori di memoria nativa e di runtime, nonché metriche di memoria che possono aiutarti a identificare il footprint della memoria di un determinato processo.

ApplicationExitInfo

ApplicationExitInfo è disponibile su Android 11 (livello API 30) o versioni successive e fornisce informazioni sul motivo dell'uscita dall'applicazione. Sono inclusi ANR, memoria insufficiente, arresti anomali delle app, utilizzo eccessivo della CPU, interruzioni dell'utente, interruzioni del sistema e modifiche alle autorizzazioni di runtime.

Modalità più restrittiva

L'utilizzo di StrictMode ti aiuta a trovare operazioni di I/O accidentali sul thread principale durante lo sviluppo della tua app. Puoi utilizzare StrictMode a livello di applicazione o attività.

Attiva finestre di dialogo ANR in background

Android mostra le finestre di dialogo ANR per le app che impiegano troppo tempo per elaborare il messaggio di trasmissione solo se l'opzione Mostra tutti gli ANR è attiva nelle Opzioni sviluppatore del dispositivo. Per questo motivo, le finestre di dialogo ANR in background non vengono sempre mostrate all'utente, anche quando l'app presenta problemi di prestazioni.

Colli di bottiglia della ricomposizione

Utilizza Android Studio Profiler e Layout Inspector per individuare i colli di bottiglia della ricomposizione. Per saperne di più, consulta Prestazioni di Jetpack Compose.

Estrai un file di tracce

Android memorizza le informazioni di traccia quando si verifica un ANR. Nelle versioni precedenti del sistema operativo, sul dispositivo è presente un singolo file /data/anr/traces.txt. Nelle versioni più recenti del sistema operativo, sono presenti più file /data/anr/anr_*. Puoi accedere alle tracce ANR da un dispositivo o un emulatore utilizzando Android Debug Bridge (ADB) come root:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

Puoi acquisire una segnalazione di bug da un dispositivo fisico utilizzando l'opzione sviluppatore Crea segnalazione di bug sul dispositivo o il comando adb bugreport sulla tua macchina di sviluppo. Per saperne di più, consulta Acquisisci e leggi le segnalazioni di bug.

Risolvi i problemi

Dopo aver identificato il problema, puoi utilizzare i suggerimenti in questa sezione per risolvere i problemi più comuni.

Codice lento nel thread principale

Identifica i punti del codice in cui il thread principale dell'app è occupato per più di 5 secondi. Cerca i casi d'uso sospetti nella tua app e prova a riprodurre l'ANR.

Un problema comune è un'attività a lunga esecuzione direttamente in un composable:

@Composable
fun BadList(rawStrings: List<String>) {
    // Math or sorting inside the composable runs on EVERY recomposition pass!
    val heavilyProcessedList = rawStrings
        .filter { it.isNotBlank() }
        .map { it.uppercase().reversed() }
        .map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
    LazyColumn { items(sortedList) { Text(it) } }
}

// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    // UI simply renders state; no heavy processing allowed here
    LazyColumn { items(uiState.sortedData) { Text(it) } }
}

I/O nel thread principale

L'esecuzione di operazioni di I/O sul thread principale è una causa comune di operazioni lente sul thread principale, che possono causare errori ANR. In Compose, gli sviluppatori spesso attivano accidentalmente letture del disco (come SharedPreferences o chiamate al database) mentre cercano di ricavare lo stato iniziale.

Esegui operazioni di I/O a lunga esecuzione lontano dal livello UI. Utilizza withContext(Dispatchers.IO) in un ViewModel o, ancora meglio, utilizza un Repository nel livello dati.

Deadlock

Un deadlock si verifica quando un thread entra in uno stato di attesa perché una risorsa richiesta è occupata da un altro thread, che a sua volta è in attesa di una risorsa occupata dal primo thread. Se il thread principale dell'app si trova in questa situazione, è probabile che si verifichino errori ANR.

I deadlock sono un fenomeno ben studiato nell'informatica ed esistono algoritmi di prevenzione dei deadlock che puoi utilizzare per evitarli.

Per saperne di più, consulta Deadlock e Algoritmi di prevenzione dei deadlock su Wikipedia.

Quando utilizzi Kotlin e Compose, puoi sostituire i blocchi primitivi con Mutex di coroutine non bloccanti (Mutex.withLock) per impedire il blocco del thread sospendendo il contesto di esecuzione anziché bloccare il thread dell'interfaccia utente. Ad esempio:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// Modern non-blocking concurrency state architecture
class SecureDataRepository {
    private val mutex = Mutex()

    suspend fun safeUIAccess() {
        // If locked, the main thread suspends seamlessly, preventing an ANR
        mutex.withLock {
            performSafeOperation()
        }
    }
}

Broadcast receiver lenti

Le app possono rispondere ai messaggi di trasmissione, ad esempio attivando o disattivando la modalità aereo o una modifica dello stato della connettività, tramite i broadcast receiver. Un ANR si verifica quando un'app impiega troppo tempo per elaborare l'annuncio.

Un errore ANR si verifica nei seguenti casi:

  • Un broadcast receiver non ha terminato l'esecuzione del metodo onReceive entro un periodo di tempo considerevole.
  • Un broadcast receiver chiama goAsync e non riesce a chiamare finish sull'oggetto PendingResult.

La tua app deve eseguire solo operazioni brevi nel metodo onReceive di un BroadcastReceiver. Tuttavia, se l'app richiede un'elaborazione più complessa come risultato di un annuncio, devi rimandare l'attività a un ViewModel (sfruttando la potenza delle coroutine, degli ambiti e dei dispatcher di Kotlin) se l'attività dovrebbe richiedere al massimo alcuni secondi, qualsiasi tipo di contenitore di stato, o a WorkManager per le attività che dovrebbero richiedere più di alcuni secondi.

GameActivity

La libreria GameActivity ha ridotto gli ANR nei case study di giochi e app scritti in C o C++. Se sostituisci l'attività nativa esistente con GameActivity, puoi ridurre il blocco del thread dell'interfaccia utente ed evitare che si verifichino alcuni ANR.

Per saperne di più sugli errori ANR, consulta Mantieni l'app reattiva. Per saperne di più sui thread, consulta Miglioramento delle prestazioni tramite threading.

Risorse aggiuntive

Visualizza contenuti

  • Nota: il testo del link viene visualizzato quando JavaScript è disattivato
  • Wakeup eccessivi