ANR

Lorsque le thread UI d'une application Android est bloqué trop longtemps, une erreur ANR (« L'application ne répond pas ») se déclenche. Si l'application est exécutée au premier plan, le système affiche une boîte de dialogue, comme illustré dans la figure 1. La boîte de dialogue ANR permet à l'utilisateur de forcer l'arrêt de l'application.

Boîte de dialogue ANR affichée à l'intention de l'utilisateur.
Figure 1. Boîte de dialogue ANR affichée

Les ANR posent problème, car le thread principal de l'application, responsable de la mise à jour de l'UI, ne peut pas traiter les événements d'entrée utilisateur ni dessiner, ce qui génère de la frustration pour l'utilisateur. Pour en savoir plus sur le thread principal de l'application, consultez la présentation des processus et des threads.

Une erreur ANR se déclenche pour votre application lorsque l'une des conditions suivantes se produit :

  • Délai d'envoi des entrées dépassé : si votre application n'a pas répondu à une entrée (comme un appui sur une touche ou un événement tactile sur l'écran) dans les cinq secondes.
  • Exécution du service : si un service déclaré par votre application ne peut pas terminer l'exécution de Service.onCreate et Service.onStartCommand/Service.onBind en quelques secondes.
  • Service.startForeground non appelé : si votre application utilise Context.startForegroundService pour démarrer un nouveau service au premier plan mais que le service n'appelle pas startForeground dans les cinq secondes.
  • Diffusion de l'intent : si l'exécution d'un BroadcastReceiver n'est pas terminée dans un délai donné. Si l'application présente une activité au premier plan, ce délai est de cinq secondes.
  • JobScheduler interactions: Si un JobService ne renvoie pas de valeur de JobService.onStartJob ou JobService.onStopJob dans les secondes qui suivent, ou si une tâche lancée par l'utilisateur démarre et que votre application n'appelle pas JobService.setNotification dans les quelques secondes suivant l'appel de JobService.onStartJob. Pour les applications ciblant Android 13 et les versions antérieures, les erreurs ANR sont silencieuses et ne sont pas signalées à l'application. Pour les applications ciblant Android 14 et versions ultérieures, les erreurs ANR sont explicites et sont signalées à l'application.

Si votre application rencontre des erreurs ANR, vous pouvez suivre les instructions de ce document pour les diagnostiquer et les résoudre.

Détecter le problème

Si vous avez déjà publié votre appli, vous pouvez utiliser Android Vitals pour consulter des informations sur les erreurs ANR. Vous pouvez utiliser d'autres outils pour détecter les erreurs ANR sur le terrain. Notez toutefois que les outils tiers ne peuvent pas signaler les ANR sur Android 10 et versions antérieures, contrairement à Android Vitals.

Android Vitals

Android Vitals vous permet de surveiller et d'améliorer le taux d'erreurs ANR de votre application. Android Vitals mesure plusieurs taux d'erreurs ANR :

  • Taux d'erreurs ANR : pourcentage d'utilisateurs actifs par jour ayant subi un type d'erreur ANR.
  • Taux d'erreurs ANR perçues par l'utilisateur : pourcentage d'utilisateurs actifs par jour qui ont subi au moins une ANR perçue par l'utilisateur. Actuellement, seules les erreurs ANR de type Input dispatching timed out sont considérées comme perçues par l'utilisateur.
  • Taux d'erreurs ANR multiples : pourcentage de vos utilisateurs actifs par jour ayant subi au moins deux erreurs ANR.

Un utilisateur actif par jour est un utilisateur unique qui se sert de votre application un seul jour sur un seul appareil, avec éventuellement plusieurs sessions. Si un utilisateur se sert de votre application sur plusieurs appareils au cours d'une même journée, chaque appareil sera comptabilisé dans le nombre d'utilisateurs actifs pour ce jour-là.

Le taux d'erreurs ANR perçues par l'utilisateur est une statistique principale Android Vitals. Il affecte donc la visibilité de votre application sur Google Play. Cette métrique est importante, car les erreurs ANR comptabilisées se produisent toujours lorsque l'utilisateur interagit avec l'appli, ce qui entraîne le plus de perturbations.

Play a défini deux seuils de comportement insatisfaisant pour cette métrique :

  • Seuil de comportement insatisfaisant en général : au moins 0, 47% des utilisateurs actifs par jour subissent une erreur ANR perçue par l'utilisateur sur tous les modèles d'appareils.
  • Seuil de comportement insatisfaisant par appareil : au moins 8% des utilisateurs actifs par jour subissent une erreur ANR perçue par l'utilisateur sur un même modèle d'appareil.

Si votre application dépasse le seuil général de comportement insatisfaisant, elle risque d'être moins visible sur tous les appareils. Si elle dépasse le seuil de comportement insatisfaisant par appareil, elle risque d'être moins visible sur le type d'appareil en question et d'afficher un avertissement dans votre fiche Play Store.

Android Vitals peut vous envoyer des alertes via la Play Console lorsque votre application subit un nombre excessif d'erreurs ANR.

Pour savoir comment Google Play collecte les données Android Vitals, consultez la Play Console documentation.

Diagnostiquer les erreurs ANR

Voici quelques tendances courantes à prendre en compte lors du diagnostic des ANR :

  • L'application effectue des opérations lentes impliquant des E/S sur le thread principal.
  • L'application effectue un calcul long sur le thread principal.
  • Le thread principal effectue un appel de liaison synchrone vers un autre processus, et le retour de cet autre processus prend beaucoup de temps.
  • Le thread principal est bloqué en attendant un blocage synchronisé pour une longue opération qui se produit sur un autre thread.
  • Le thread principal se trouve dans un interblocage avec un autre thread, soit au cours de votre processus, soit via un appel de liaison. Le thread principal n'attend pas que l'opération longue se termine. Il se trouve dans une situation d'interblocage.

Les techniques suivantes peuvent vous aider à déterminer la cause de vos erreurs ANR.

HealthStats

HealthStats fournit des métriques sur l'état d'une application en capturant le temps système et utilisateur total, le temps CPU, le réseau, les métriques radio, le temps d'activation/de désactivation de l'écran et les alarmes de réveil. Cela peut vous aider à mesurer l'utilisation globale du processeur et la décharge de la batterie.

Déboguer

Debug permet d'inspecter les applications Android pendant le développement, y compris le traçage et le nombre d'allocations pour identifier les à-coups et la latence dans les applis. Vous pouvez également utiliser Debug pour obtenir des compteurs d'exécution et de mémoire native, ainsi que des métriques de mémoire qui peuvent aider à identifier l'espace mémoire utilisé d'un processus particulier.

ApplicationExitInfo

ApplicationExitInfo est disponible sur Android 11 (niveau d'API 30) ou version ultérieure et fournit des informations sur le motif de fermeture d'une application. Cela inclut les erreurs ANR, une mémoire insuffisante, les plantages d'application, l'utilisation excessive du processeur, les interruptions utilisateur, les interruptions système ou les modifications des autorisations d'exécution.

Mode strict

Using StrictMode vous aide à détecter les opérations d'E/S accidentelles sur le thread principal pendant le développement de votre appli. Vous pouvez utiliser StrictMode au niveau de l'application ou de l'activité.

Activer les boîtes de dialogue ANR en arrière-plan

Android affiche des boîtes de dialogue ANR pour les applications qui mettent trop de temps à traiter le message de diffusion uniquement si l'option Afficher toutes les erreurs ANR est activée dans les Options pour les développeurs de l'appareil. Pour cette raison, les boîtes de dialogue ANR en arrière-plan ne sont pas toujours présentées à l'utilisateur, même lorsque l'application rencontre des problèmes de performances.

Goulots d'étranglement de la recomposition

Utilisez le Profileur Android Studio et l'outil d'inspection de la mise en page pour identifier les goulots d'étranglement de la recomposition. Pour en savoir plus, consultez la page Performances de Jetpack Compose.

Récupérer un fichier de traces

Android stocke les informations de trace en cas d'erreur ANR. Pour les versions d'OS plus anciennes, un seul fichier /data/anr/traces.txt est installé sur l'appareil. Dans les versions d'OS plus récentes, il existe plusieurs fichiers /data/anr/anr_*. Vous pouvez accéder aux traces ANR d'un appareil ou d'un émulateur en utilisant Android Debug Bridge (adb) en tant que racine :

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

Vous pouvez générer un rapport de bug à partir d'un appareil physique à l'aide de l'option « Créer un rapport de bug » du développeur sur l'appareil ou de la commande adb bugreport sur votre ordinateur de développement. Pour en savoir plus, consultez Capturer et lire les rapports de bug.

Résoudre les problèmes

Une fois que vous avez identifié le problème, vous pouvez utiliser les conseils de cette section pour le résoudre.

Lenteur du code dans le thread principal

Identifiez les emplacements de votre code où le thread principal de l'application est occupé pendant plus de cinq secondes. Recherchez les cas d'utilisation suspects dans votre application et essayez de reproduire l'erreur ANR.

Un problème courant est une tâche de longue durée directement dans 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) } }
}

E/S sur le thread principal

L'exécution d'opérations d'E/S sur le thread principal est une cause fréquente de lenteur, ce qui peut entraîner des erreurs ANR. Dans Compose, les développeurs déclenchent souvent accidentellement des lectures de disque (comme SharedPreferences ou des appels de base de données) lorsqu'ils tentent de dériver l'état initial.

Exécutez les opérations d'E/S de longue durée en dehors de la couche UI. Utilisez withContext(Dispatchers.IO) dans un ViewModel ou, mieux encore, utilisez un Repository sur la couche de données.

Interblocages

Un interblocage se produit lorsqu'un thread passe à l'état d'attente, car une ressource requise est détenue par un autre thread, qui attend également une ressource détenue par le premier thread. Si le thread principal de l'application se trouve dans cette situation, il est probable que des erreurs ANR se produisent.

Les interblocages sont un phénomène bien étudié en informatique. Il existe des algorithmes de prévention des interblocages que vous pouvez utiliser pour éviter ce problème.

Pour en savoir plus, consultez les articles sur l'interblocage et la prévention des interblocages sur Wikipédia.

Lorsque vous utilisez Kotlin et Compose, vous pouvez remplacer les verrous primitifs par des mutex de coroutine non bloquants (Mutex.withLock) pour éviter le blocage des threads en suspendant le contexte d'exécution au lieu de figer le thread UI. Exemple :

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 receivers lents

Les applications peuvent répondre aux messages de diffusion, par exemple en activant ou en désactivant le mode Avion, ou en cas de changement d'état de la connectivité, à l'aide de broadcast receivers. Une erreur ANR se produit lorsqu'une application prend trop de temps pour traiter le message de diffusion.

Une erreur ANR se produit dans les cas suivants :

  • Un broadcast receiver n'a pas fini d'exécuter sa onReceive méthode dans un délai considérable.
  • Un broadcast receiver appelle goAsync et ne parvient pas à appeler finish sur l'objet PendingResult.

Votre application ne doit effectuer des opérations courtes que dans la onReceive méthode d'un BroadcastReceiver. Toutefois, si votre application nécessite un traitement plus complexe à la suite d'un message de diffusion, vous devez reporter la tâche vers un ViewModel (en exploitant la puissance des coroutines, des étendues et des coordinateurs Kotlin) si la tâche devrait prendre quelques secondes au maximum, tout type de détenteur d'état, ou vers WorkManager pour les tâches qui devraient prendre plus de quelques secondes.

GameActivity

La bibliothèque GameActivity a réduit le nombre d'erreurs ANR dans les études de cas de jeux et d'applications écrits en C ou C++. Si vous remplacez votre activité native existante par GameActivity, vous pouvez réduire le blocage des threads UI et empêcher certaines erreurs ANR.

Pour en savoir plus sur les erreurs ANR, consultez Garder une application responsive. Pour en savoir plus sur les threads, consultez Améliorer les performances grâce aux threads.

Ressources supplémentaires

Afficher le contenu