ANRs
On octobre 14, 2021 by adminLorsque le thread de l’interface utilisateur d’une application Android est bloqué trop longtemps, une erreur « ApplicationNot Responding » (ANR) est déclenchée. Si l’application est au premier plan, le système affiche une boîte de dialogue pour l’utilisateur, comme le montre la figure 1. La boîte de dialogue ANR donne à l’utilisateur la possibilité de quitter de force l’app.
Figure 1. Dialogue ANR affiché à l’utilisateur
Les ANR sont un problème parce que le thread principal de l’app, qui est responsable de la mise à jour de l’UI, ne peut pas traiter les événements d’entrée de l’utilisateur ou dessiner, ce qui provoque la frustration de l’utilisateur. Pour plus d’informations sur le thread principal de l’app, consultez la section Processus et threads.
Un ANR sera déclenché pour votre app lorsque l’une des conditions suivantes se produit :
- Alors que votre activité est au premier plan, votre app n’a pas répondu à un événement d’entrée ou
BroadcastReceiver
(tel que des événements de pression de touche ou de toucher d’écran) dans les 5 secondes. - Alors que vous n’avez pas d’activité au premier plan, votre
BroadcastReceiver
n’a pas fini de s’exécuter dans un délai aconsidérable.
Si votre app connaît des ANR, vous pouvez utiliser les conseils de cet article pour diagnostiquer et corriger le problème.
Détecter et diagnostiquer les problèmes
Android fournit plusieurs moyens de vous faire savoir que votre application a un problème, et de vous aider à le diagnostiquer.Si vous avez déjà publié votre application, Androidvitals peut vous alerter que le problème se produit, etil existe des outils de diagnostic pour vous aider à trouver le problème.
Android vitals
Android vitals peut aider à améliorer les performances de votre application en vous alertant, via la console de jeu, lorsque votre application présente des ANRs excessifs.Android vitals considère que les RNA sont excessifs lorsqu’une appli :
- expose au moins un RNA dans au moins 0,47 % de ses sessions quotidiennes.
- expose 2 RNA ou plus dans au moins 0,24 % de ses sessions quotidiennes.
Une session quotidienne fait référence à un jour au cours duquel votre appli a été utilisée.
Pour plus d’informations sur la manière dont Google Play collecte les données vitales d’Android, consultez la documentation de la consolePlay.
Diagnostic des RNA
Il existe des modèles communs à rechercher lors du diagnostic des RNA :
- L’appli effectue des opérations lentes impliquant des E/S sur le thread principal.
- L’appli effectue un long calcul sur le thread principal.
- Le thread principal effectue un appel binder synchrone à un autre processus, et cet autre processus prend beaucoup de temps à revenir.
- Le thread principal est bloqué en attendant un bloc synchronisé pour une longue opération qui se passe sur un autre thread.
- Le thread principal est dans une impasse avec un autre thread, soit dans votre processus, soit via un appel binder. Le thread principal ne se contente pas d’attendre la fin d’une longue opération, mais se trouve dans une situation de deadlock. Pour plus d’informations, voir Deadlock sur Wikipedia.
Les techniques suivantes peuvent vous aider à trouver laquelle de ces causes est à l’origine de vos ANR.
Mode strict
L’utilisation de StrictMode
vous aide à trouver les opérations d’E/S accidentelles sur le thread principal pendant que vous développez votre application. Vous pouvez utiliser StrictMode
au niveau de l’application ou de l’activité.
Activer les dialogues ANR en arrière-plan
Android affiche les dialogues ANR pour les apps qui prennent trop de temps pour traiter le message de diffusion uniquement si l’option Afficher tous les ANR est activée dans les Developeroptions de l’appareil. Pour cette raison, les dialogues ANR en arrière-plan ne sont pas toujours affichés à l’utilisateur, mais l’app pourrait tout de même rencontrer des problèmes de performance.
Traceview
Vous pouvez utiliser Traceview pour obtenir une trace de votre app en cours d’exécution tout en passant par les cas d’utilisation et identifier les endroits où le thread principal est occupé. Pour plus d’informations sur l’utilisation de Traceview, voir Profiler avec Traceview etdmtracedump.
Tirer un fichier de traces
Android stocke des informations de traces lorsqu’il subit un ANR. Sur les anciennes versions du système d’exploitation, il y a un seul fichier /data/anr/traces.txt
sur le périphérique.Sur les versions plus récentes du système d’exploitation, il y a plusieurs fichiers /data/anr/anr_*
.Vous pouvez accéder aux traces ANR à partir d’un périphérique ou d’un émulateur en utilisantAndroid Debug Bridge (adb) en tant que root :
adb rootadb shell ls /data/anradb pull /data/anr/<filename>
Vous pouvez capturer un rapport de bogue à partir d’un périphérique physique en utilisant soit l’option Take bugreport developer sur le périphérique, soit la commande adb bugreport sur votre machine de développement. Pour plus d’informations, consultez la section Capturer et lire les rapports de bogue.
Réparer les problèmes
Après avoir identifié le problème, vous pouvez utiliser les conseils de cette section pour réparer les problèmes couramment rencontrés.
Code lent sur le thread principal
Identifiez les endroits de votre code où le thread principal de l’application est occupé pendant plus de 5 secondes. Recherchez les cas d’utilisation suspects dans votre application et essayez dereproduire le RNA.
Par exemple, la figure 2 montre une timeline Traceview où le thread principal est occupé pendant plus de 5 secondes.
Figure 2. Ligne de temps Traceview montrant un thread principal occupé
La figure 2 nous montre que la plupart du code incriminé se produit dans le handler onClick(View)
, comme le montre l’exemple de code suivant :
Dans ce cas, vous devriez déplacer le travail qui s’exécute dans le thread principal vers un workerthread. Le Framework Android comprend des classes qui peuvent aider à déplacer la tâche vers un workerthread. Consultez Threading on Android pour plus d’informations.
IO sur le thread principal
L’exécution d’opérations IO sur le thread principal est une cause fréquente de lenteur des opérations sur le thread principal, ce qui peut provoquer des ANR. Il est recommandé de déplacer toutes les opérations IO vers un thread worker, comme indiqué dans la section précédente.
Certains exemples d’opérations IO sont les opérations de réseau et de stockage. Pour plus d’informations, voir Exécution des opérations réseau et Sauvegarde des données.
Conflit de verrou
Dans certains scénarios, le travail qui provoque l’ANR n’est pas directement exécuté sur le thread principal de l’application. Si un thread de travail détient un verrou sur une ressource dont le thread principal a besoin pour terminer son travail, alors un ANR peut se produire.
Par exemple, la figure 4 montre une timeline Traceview où la plupart du travail est exécuté sur un thread de travail.
Figure 4. Ligne de temps Traceview qui montre le travail exécuté sur un workerthread
Mais si vos utilisateurs rencontrent toujours des ANR, vous devez regarder l’état du thread principal dans Android Device Monitor. Habituellement, le thread principal est dans l’étatRUNNABLE
s’il est prêt à mettre à jour l’interface utilisateur et est généralement réactif.
Mais si le thread principal ne peut pas reprendre son exécution, alors il est dans l’état BLOCKED
et ne peut pas répondre aux événements. Le statut s’affiche sur Android Device Monitor comme Monitor ou Wait, comme le montre la figure 5.
Figure 5. Main thread dans l’état Monitor
La trace suivante montre le thread principal d’une application qui est bloqué en attendant aresource:
L’examen de la trace peut vous aider à localiser le code qui bloque le thread principal.Le code suivant est responsable de la détention du verrou qui bloque le mainthread dans la trace précédente:
Un autre exemple est le thread principal d’une application qui attend un résultat d’un threadworker, comme indiqué dans le code suivant. Notez que l’utilisation de wait()
etnotify()
n’est pas un modèle recommandé dans Kotlin, qui a ses propres mécanismes pour gérer la concurrence. Lorsque vous utilisez Kotlin, vous devriez utiliser des mécanismes spécifiques à Kotlin si possible.
Il existe quelques autres situations qui peuvent bloquer le thread principal, y compris les threads qui utilisent Lock
, Semaphore
, ainsi qu’un pool de ressources (tel qu’un pool de connexions de base de données) ou d’autres mécanismes d’exclusion mutuelle (mutex).
Vous devriez évaluer les verrous que votre application détient sur les ressources en général, mais si vous voulez éviter les ANR, alors vous devriez regarder les verrous détenus pour les ressources requises par le thread principal.
Assurez-vous que les verrous sont détenus pour le moins de temps possible, ou encore mieux,évaluez si l’application a besoin du verrouillage en premier lieu. Si vous utilisez le verrou pour déterminer quand mettre à jour l’interface utilisateur en fonction du traitement d’un thread de travail, utilisez des mécanismes tels que onProgressUpdate()
et onPostExecute()
pour communiquer entre le thread de travail et le thread principal.
Deadlocks
Un deadlock se produit lorsqu’un thread entre dans un état d’attente parce qu’une ressource requise est retenue par un autre thread, qui attend également une ressource retenue par le premier thread. Si le thread principal de l’application se trouve dans cette situation, des ANR sont susceptibles de se produire.
Les deadlocks sont un phénomène bien étudié en informatique, et il existe des algorithmes de prévention des deadlocks que vous pouvez utiliser pour éviter les deadlocks.
Pour plus d’informations, voir Deadlock etDeadlock preventionalgorithms surWikipedia.
Récepteurs de diffusion lente
Les apps peuvent répondre aux messages de diffusion, tels que l’activation ou la désactivation du mode avion ou un changement d’état de connectivité, au moyen de récepteurs de diffusion. Un ANR se produit lorsqu’une appli prend trop de temps pour traiter le message de diffusion.
Un ANR se produit dans les cas suivants :
- Un récepteur de diffusion n’a pas fini d’exécuter sa méthode
onReceive()
dans un délai aconsidérable. - Un récepteur de diffusion appelle
goAsync()
et ne parvient pas à appelerfinish()
sur l’objetPendingResult
.
Votre application ne devrait effectuer que des opérations courtes dans la méthode onReceive()
d’un BroadcastReceiver
. Cependant, si votre application nécessite un traitement plus complexe à la suite d’un message de diffusion, vous devez reporter la tâche à un IntentService
.
Vous pouvez utiliser des outils comme Traceview pour identifier si votre récepteur de diffusion exécute des opérations de longue durée sur le thread principal de l’application. Par exemple, la figure 6 montre la chronologie d’un récepteur de diffusion qui traite un message sur le thread principal pendant environ 100 secondes.
Figure 6. Traceview timeline montrant le travail du récepteur de diffusion sur le mainthread
Ce comportement peut être causé par l’exécution d’opérations à longue exécution sur la méthode onReceive()
du BroadcastReceiver
, comme le montre l’exemple suivant :
Dans des situations comme celles-ci, il est recommandé de déplacer l’opération à longue exécution vers un IntentService
car il utilise un worker thread pour exécuter son travail. Le code suivant montre comment utiliser un IntentService
pour traiter une opération longue durée :
A la suite de l’utilisation du IntentService
, l’opération longue durée est exécutée sur un fil de travail au lieu du fil principal. La figure 7 montre le travail reporté sur le thread de travail dans la ligne de temps Traceview.
Figure 7. Ligne de temps Traceview montrant le message de diffusion traité sur un threadworker
Votre récepteur de diffusion peut utiliser goAsync()
pour signaler au système qu’il a besoin de plus de temps pour traiter le message. Cependant, vous devez appeler finish()
sur l’objet PendingResult
. L’exemple suivant montre comment appeler finish() pour laisser le système recycler le récepteur de diffusion et éviter un ANR :
Cependant, déplacer le code d’un récepteur de diffusion lent vers un autre thread et utiliser goAsync()
ne résoudra pas l’ANR si la diffusion est en arrière-plan. Le délai d’expiration de l’ANR s’applique toujours.
Pour plus d’informations sur les ANR, consultez la sectionKeeping your app responsive. Pour plus d’informations sur les threads, consultez la section Performances du threading.
Laisser un commentaire