Lorsque le thread UI d'une application Android est bloqué trop longtemps, le système déclenche une erreur ANR (« L'application ne répond pas »). Cette page décrit les différents types d'erreurs ANR, explique comment les diagnostiquer et propose des solutions pour les résoudre. Toutes les plages de délais avant expiration par défaut s'appliquent aux appareils AOSP et Pixel. Ces délais peuvent varier selon les OEM.
Notez bien que, lorsque vous déterminez la cause des erreurs ANR, il est utile de faire la distinction entre les problèmes du système et ceux de l'application.
Lorsque le système est dans un état défectueux, les problèmes suivants peuvent entraîner des erreurs ANR :
- Les problèmes temporaires sur le serveur système ralentissent généralement les appels de liaison.
- Des problèmes liés au serveur système et à une charge élevée de l'appareil empêchent la planification des threads d'application.
Le cas échéant, un bon moyen de faire la distinction entre les problèmes du système et ceux de l'application consiste à utiliser des traces de Perfetto :
- Vérifiez si le thread principal de l'application est planifié en consultant le suivi de l'état du thread dans Perfetto pour voir s'il est en cours d'exécution ou exécutable.
- Recherchez d'éventuels problèmes dans les threads
system_server, tels que les conflits de verrouillage. - Pour les appels de liaison lents, examinez le thread de réponse s'il est présent pour en comprendre la raison.
Délai avant expiration de distribution des entrées
Les erreurs ANR de distribution des entrées se produisent lorsque le thread principal de l'application ne répond pas à temps à un événement d'entrée, tel qu'un balayage ou une pression sur une touche. Étant donné que l'application est exécutée au premier plan lorsque des délais avant expiration de distribution des entrées se produisent, ils sont presque toujours visibles par l'utilisateur, et il est important de les limiter.
Délai avant expiration par défaut : 5 secondes.
Les erreurs ANR de distribution des entrées sont généralement causées par des problèmes sur le thread principal. Si le thread principal a été bloqué en attendant l'obtention d'un verrou, le thread du conteneur peut également être impliqué.
Pour éviter les erreurs ANR de distribution des entrées, suivez ces bonnes pratiques :
- N'effectuez pas d'opérations bloquantes ou de longue durée sur le thread principal. Envisagez d'utiliser
StrictModepour détecter toute activité accidentelle sur le thread principal. - Minimisez les conflits de verrouillage entre le thread principal et les autres threads.
- Réduisez les tâches autres que celles liées à l'interface utilisateur sur le thread principal, par exemple lors de la gestion des broadcasts ou de l'exécution de services.
Causes courantes
Voici quelques causes courantes et suggestions de solutions pour les erreurs ANR de distribution des entrées.
| Cause | Que se passe-t-il | Solutions suggérées |
|---|---|---|
| Appel de liaison lent | Le thread principal effectue un long appel de liaison synchrone. | Déplacez l'appel en dehors du thread principal ou essayez de l'optimiser, si vous êtes propriétaire de l'API. |
| Nombreux appels de liaison consécutifs | Le thread principal effectue de nombreux appels de liaison synchrones consécutifs. | N'effectuez pas d'appels de liaison dans une boucle étroite. |
| E/S bloquantes | Le thread principal effectue des appels d'E/S bloquants, tels que l'accès à la base de données ou au réseau. | Retirez toutes les E/S bloquantes du thread principal. |
| Conflits de verrouillage | Le thread principal est bloqué en attendant d'obtenir un verrou. | Réduisez les conflits de verrouillage entre le thread principal et un thread différent. Optimisez le code lent dans un thread différent. |
| Frame coûteux en ressources | Trop d'affichages sur un seul frame, ce qui entraîne de graves à-coups | Simplifiez l'affichage du frame. N'utilisez pas les algorithmes n2. Utilisez des composants efficaces pour le défilement ou la pagination, comme la bibliothèque Paging de Jetpack. |
| Bloqué par un autre composant | Un autre composant, tel qu'un broadcast receiver, est en cours d'exécution et bloque le thread principal. | Retirez autant que possible les tâches qui ne sont pas liées à l'UI du thread principal. Exécutez des broadcast receivers sur un thread différent. |
| Blocage du GPU | Le blocage du GPU est un problème système ou matériel qui entraîne le blocage de l'affichage et, par conséquent, une erreur ANR de distribution des entrées. | Malheureusement, il n'existe généralement pas de solutions du côté de l'application. Si possible, contactez l'équipe chargée du matériel pour résoudre le problème. |
Comment déboguer
Commencez le débogage en examinant la signature du groupe d'erreurs ANR dans la Google Play Console ou dans Firebase Crashlytics. Le groupe contient généralement les frames supérieurs susceptibles de provoquer l'erreur ANR.
L'organigramme suivant montre comment déterminer la cause d'une erreur ANR du délai avant expiration de distribution des entrées.
Play Vitals peut détecter et déboguer certaines de ces causes d'erreurs ANR courantes. Par exemple, si Vitals détecte qu'une erreur ANR s'est produite en raison d'un conflit de verrouillage, l'appli peut résumer le problème et la solution recommandée dans la section Insights sur les erreurs ANR.
Aucune fenêtre sélectionnée
Alors que les événements tels que les gestes tactiles sont envoyés directement à la fenêtre appropriée en fonction des tests de positionnement, les événements comme les touches ont besoin d'une cible. Cette cible est appelée fenêtre sélectionnée. Il n'y a qu'une seule fenêtre sélectionnée par écran, qui correspond généralement à celle avec laquelle l'utilisateur interagit actuellement. Si une fenêtre sélectionnée ne peut être trouvée, l'entrée génère une erreur ANR de fenêtre sans sélection. Une erreur ANR de fenêtre sans sélection est un type d'erreur ANR de distribution des entrées.
Délai avant expiration par défaut : 5 secondes.
Causes courantes
Les erreurs ANR de fenêtre sans sélection sont généralement causées par l'un des problèmes suivants :
- L'appli effectue trop de tâches et est trop lente pour dessiner le premier frame.
- La fenêtre principale n'est pas sélectionnable. Si une fenêtre est signalée par l'icône
FLAG_NOT_FOCUSABLE, l'utilisateur ne peut pas lui envoyer d'événements de touche ou de bouton.
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); }
Délai avant expiration du broadcast receiver
Une erreur ANR de broadcast receiver se produit lorsqu'un broadcast receiver ne gère pas une diffusion à temps. Pour les récepteurs synchrones ou qui n'appellent pas goAsync(), un délai avant expiration signifie que onReceive() ne s'est pas terminé à temps. Pour les récepteurs asynchrones, ou les récepteurs qui appellent goAsync(), un délai avant expiration signifie que PendingResult.finish() n'a pas été appelé à temps.
Les erreurs ANR du broadcast receiver se produisent souvent dans les threads suivants :
- Thread principal, en cas de démarrage lent de l'application.
- Thread exécutant un broadcast receiver, si le problème est une lenteur du code
onReceive(). - Diffusion des threads de travail, si le problème est une lenteur du code de diffusion
goAsync().
Pour éviter les erreurs ANR du broadcast receiver, suivez ces bonnes pratiques :
- Assurez-vous que l'application démarre rapidement, car elle est comptabilisée dans le délai avant expiration de l'erreur ANR si l'application est lancée pour gérer la diffusion.
- Si
goAsync()est utilisé, assurez-vous quePendingResult.finish()est appelé rapidement. Elle est soumise au même délai avant expiration de l'erreur ANR que les broadcast receivers synchrones. - Si
goAsync()est utilisé, assurez-vous que le ou les threads de travail ne sont pas partagés avec d'autres opérations de longue durée ou bloquantes. - Envisagez d'utiliser
registerReceiver()pour exécuter des broadcast receivers dans un thread non principal afin d'éviter de bloquer le code de l'interface utilisateur exécuté dans le thread principal.
Délais avant expiration
Les délais avant expiration des broadcast receivers varient selon que l'indicateur d'intent de premier plan est défini ou non, et selon la version de la plate-forme.
| Type d'intent | Android 13 ou version antérieure | Android 14 ou version ultérieure |
|---|---|---|
Intent de priorité au premier plan ( |
10 secondes |
10 à 20 secondes, selon que le processus manque de ressources de processeur ou non |
Intent de priorité en arrière-plan ( |
60 secondes |
60 à 120 secondes, selon que le processus manque de ressources de processeur ou non |
Pour savoir si l'indicateur FLAG_RECEIVER_FOREGROUND est défini, recherchez "flg=" dans l'objet de l'erreur ANR et vérifiez la présence de 0x10000000. Si ce bit est défini, FLAG_RECEIVER_FOREGROUND est défini pour l'intent, ce qui réduit le délai avant expiration.
Exemple d'objet de l'erreur ANR avec un délai avant expiration de diffusion court (10 à 20 secondes) :
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Exemple d'objet de l'erreur ANR avec un délai avant expiration de diffusion long (de 60 à 120 secondes) :
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
Comment les temps de diffusion sont-ils mesurés ?
La mesure de la durée de diffusion commence lorsque la diffusion est envoyée depuis system_server vers l'application et se termine lorsque l'application a terminé le traitement de la diffusion. Si le processus de l'application n'était pas déjà en cours d'exécution, elle doit également effectuer un démarrage à froid pendant le délai avant expiration de l'erreur ANR. Par conséquent, un démarrage lent de l'application peut entraîner des erreurs ANR du broadcast receiver.
L'image suivante illustre la chronologie des erreurs ANR du broadcast receiver sur certains processus d'application.
La mesure du délai avant expiration de l'erreur ANR se termine lorsque le récepteur termine le traitement de la diffusion. Le moment exact où cela se produit dépend du type de récepteur synchrone ou asynchrone.
- Pour les récepteurs synchrones, la mesure s'arrête lorsque
onReceive()est renvoyé. - Pour les récepteurs asynchrones, la mesure s'arrête lorsque
PendingResult.finish()est appelé.
Causes courantes
Voici quelques causes courantes et suggestions de solutions pour les erreurs ANR du broadcast receiver.
| Cause | S'applique à | Que s'est-il passé | Solution suggérée |
|---|---|---|---|
| Démarrage lent de l'appli | Tous les récepteurs | L'application a mis trop de temps à effectuer un démarrage à froid. | Optimisez le démarrage lent de l'application. |
onReceive() non planifié | Tous les récepteurs | Le thread du broadcast receiver était occupé à effectuer d'autres tâches et n'a pas pu démarrer la méthode onReceive(). | N'effectuez pas de tâches de longue durée sur le thread récepteur (ou déplacez le récepteur vers un thread dédié). |
onReceive() lent | Tous les récepteurs, mais principalement les récepteurs synchrones | La méthode onReceive() a démarré, mais a été bloquée ou lente, et ne s'est donc pas terminée à temps. | Optimisez le code du récepteur lent. |
| Tâches du récepteur asynchrone non planifiées | Récepteurs goAsync() | La méthode onReceive() a tenté d'exécuter une tâche sur un pool de threads de travail bloqué. Par conséquent, la tâche n'a jamais démarré. |
Optimisez les appels lents ou bloquants, ou utilisez des threads différents pour les workers de diffusion par rapport aux autres tâches de longue durée. |
| Workers lents ou bloqués | Récepteurs goAsync() |
Une opération bloquante ou lente s'est produite dans le pool de threads de travail lors du traitement de la diffusion. PendingResult.finish n'a donc pas été appelé à temps. | Optimisez le code lent du récepteur async. |
Vous avez oublié d'appeler PendingResult.finish |
Récepteurs goAsync() |
Il manque l'appel à finish() dans le chemin de code. |
Assurez-vous que finish() est toujours appelé. |
Comment déboguer
Sur la base de la signature du groupe et du rapport ANR, vous pouvez localiser le thread sur lequel le récepteur s'exécute, puis le code spécifique qui est manquant ou qui s'exécute lentement.
L'organigramme suivant montre comment déterminer la cause d'une erreur ANR sur le broadcast receiver.
Trouver le code du récepteur
La Google Play Console affiche la classe du récepteur et l'intent de diffusion dans la signature de l'erreur ANR. Recherchez les éléments suivants :
cmp=<receiver class>act=<broadcast_intent>
Voici un exemple de signature de l'erreur ANR d'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 }
Rechercher le thread qui exécute la méthode onReceive()
Si vous utilisez Context.registerReceiver pour spécifier un gestionnaire personnalisé, c'est le thread qui exécute ce gestionnaire. Sinon, il s'agit du thread principal.
Exemple : Tâches de récepteur asynchrone non planifiées
Cette section présente un exemple de débogage d'une erreur ANR d'un broadcast receiver.
Supposons que la signature de l'erreur ANR se présente comme suit :
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
D'après la signature, il semble que l'intent de diffusion soit android.accounts.LOG_ACCOUNTS_CHANGED et que la classe du récepteur soit com.example.app.MyReceiver.
À partir du code du récepteur, vous pouvez déterminer que le pool de threads "BG Thread [0,1,2,3]" effectue la tâche principale pour traiter cette diffusion. En examinant les vidages de pile, vous pouvez constater que les quatre threads d'arrière-plan (BG) ont le même schéma ; ils exécutent un appel bloquant, getDataSync. Étant donné que tous les threads de BG étaient occupés, la diffusion n'a pas pu être traitée à temps, ce qui a entraîné une erreur 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
getDataSyncis slow and optimize. - Don't run
getDataSyncon 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
goAsyncworker 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(), andonBind()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.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
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.MyServiceDetermine 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.handleBindApplicationApp 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 theMyServiceclass 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)Si vous ne voyez aucun des appels de fonction importants, il existe plusieurs autres possibilités :
- Le service est en cours d'exécution ou en cours d'arrêt, ce qui signifie que les piles sont prises trop tard. Dans ce cas, vous pouvez ignorer l'ANR en tant que faux positif.
- Un autre composant d'application est en cours d'exécution, comme un broadcast receiver. Dans ce cas, le thread principal est probablement bloqué dans ce composant, ce qui empêche le démarrage du service.
Si vous voyez un appel de fonction clé et pouvez déterminer où l'erreur ANR se produit généralement, vérifiez le reste des piles de threads principaux pour identifier l'opération lente, et optimisez-la ou retirez-la du chemin critique.
- Assurez-vous que l'application démarre rapidement, car elle est comptabilisée dans le délai avant expiration de l'erreur ANR si l'application est lancée pour exécuter le fournisseur de contenu.
- Assurez-vous que les requêtes du fournisseur de contenu sont rapides.
- N'effectuez pas de nombreux appels de liaison bloquants simultanés qui peuvent bloquer tous les threads de liaison de l'application.
- Gel silencieux de l'UI : l'UI de l'application ne répond plus aux entrées utilisateur.
- Plantages : l'application peut planter avec un
illegalStateException. - ANR : le système peut déclencher un délai avant expiration ANR.
- Planification des traversées :
ViewRootImplplanifie une traversée (une passe de mise en page et de dessin) en publiant une barrière de synchronisation sur leMessageQueuedu thread d'UI. Cette barrière met en pause le traitement normal des messages afin que la mise en page de l'UI puisse être prioritaire. - Condition de concurrence : lorsque plusieurs threads tentent d'invalider une vue simultanément, ils se font concurrence pour planifier cette traversée. Les deux threads peuvent insérer une barrière de synchronisation, mais le framework ne stocke le jeton que pour l'un d'eux.
- Blocage de l'UI : lorsque la traversée s'exécute, elle ne supprime que la barrière enregistrée. Les barrières secondaires "divulguées" restent indéfiniment dans la file d'attente, bloquant définitivement le thread d'UI et l'empêchant de traiter les messages synchrones.
- N'interagissez qu'avec les objets
View(y compris en lisant des propriétés commewidthouheight, ou en définissant des propriétés commeTextView's text) sur le thread où vous avez créé la hiérarchie des vues. Il s'agit presque toujours du thread principal ou du thread UI. - Si vous ne pouvez pas garantir que vous exécutez sur le thread UI lorsque vous accédez aux vues, supposez que ce n'est pas sûr. Par exemple, si vous utilisez
Coroutinespour le travail en arrière-plan, vous devez passer au répartiteur principal avant de manipuler les objetsView. ContextCompat#getMainExecutor(android.content.Context)View.post(Runnable)Activity.runOnUiThread(Runnable)- Installez une version débogable de votre application sur un appareil équipé d'Android 17 ou version ultérieure.
- Sur votre appareil, ouvrez l'application Paramètres et accédez à Système > Paramètres avancés > Options pour les développeurs > Modifications de compatibilité des applications.
- Sélectionnez votre application dans la liste.
- Dans la liste des modifications, recherchez et activez le bouton
ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS. - Télémétrie et suivi : ce listener permet à votre application de recevoir des rappels lorsqu'une API View est appelée à partir d'un thread incorrect, ce qui facilite la journalisation de la télémétrie et le suivi des bugs.
- Exécution intégrée : l'écouteur est appelé en ligne, ce qui vous permet de capturer et d'inspecter la trace de la pile exacte au moment où le non-respect se produit.
- Problème au niveau du système. Le processus n'a pas été planifié en raison d'une charge système importante ou d'un problème au niveau du serveur système.
- Vidage tardif de la pile. Le thread a été récupéré pendant la courte période entre le déclenchement de l'ANR et le vidage des piles. La latence dans Pixels sur Android 13 est d'environ 100 ms, mais peut dépasser 1 s. Sous Android 14, la latence dans Pixels est généralement inférieure à 10 ms.
- Erreur d'attribution du fil de discussion. Le thread utilisé pour créer la signature de l'erreur ANR n'était pas celui qui n'a pas répondu à l'origine de l'erreur ANR.
- Afficher l'infraction liée au threading Si votre application modifie une vue sur un thread d'arrière-plan, elle peut déclencher une condition de concurrence dans les éléments internes de la vue, ce qui empêche l'exécution des tâches du thread UI.
- Forte charge système : évaluez la pression globale exercée sur les ressources de l'appareil, comme la famine du processeur, de la mémoire ou des E/S à l'échelle du système, comme cause principale du manque de réactivité.
- Attribution incorrecte de thread : auditez les threads de worker et de binder pour détecter les blocages, la contention de verrou affectant le thread principal ou les threads d'arrière-plan suspendus traitant les composants asynchrones (par exemple,
goAsync()). - Afficher les cas de non-respect du threading : analysez les threads d'arrière-plan pour détecter les modifications illégales de la hiérarchie des vues d'UI, qui peuvent rendre une barrière de synchronisation orpheline dans MessageQueue et bloquer définitivement tous les messages synchrones.
- La pile prend trop de temps et dépasse le délai.
- Le processus a expiré ou a été arrêté avant la prise des piles.
Pour en savoir plus sur les services, consultez les pages suivantes :
Le fournisseur de contenu ne répond pas
Une erreur ANR de fournisseur de contenu se produit lorsqu'un fournisseur de contenu distant prend plus de temps que le délai avant expiration pour répondre à une requête et qu'il est arrêté.
Délai avant expiration par défaut : Spécifié par le fournisseur de contenu à l'aide de ContentProviderClient.setDetectNotResponding. Le délai avant expiration de l'erreur ANR inclut la durée totale d'exécution d'une requête de fournisseur de contenu distant, ce qui comprend le démarrage à froid de l'application à distance si elle n'était pas déjà en cours d'exécution.
Pour éviter les erreurs ANR de fournisseur de contenu, suivez ces bonnes pratiques :
Causes courantes
Le tableau suivant présente les causes courantes d'erreurs ANR de fournisseur de contenu et les solutions suggérées.
| Cause | Que se passe-t-il | Signal | Solution suggérée |
|---|---|---|---|
| Requête du fournisseur de contenu lente | L'exécution du fournisseur de contenu prend trop de temps ou est bloquée. | Le frame android.content.ContentProvider$Transport.query se trouve dans le thread de liaison. |
Optimisez la requête du fournisseur de contenu. Identifiez ce qui bloque le thread de liaison. |
| Démarrage lent de l'appli | Le démarrage de l'application du fournisseur de contenu prend trop de temps. | Le frame ActivityThread.handleBindApplication se trouve dans le thread principal. |
Optimisez le démarrage de l'application. |
| Épuisement du thread de liaison : Tous les threads de liaison sont occupés | Tous les threads de liaison étant occupés à diffuser d'autres requêtes synchrones, l'appel de liaison du fournisseur de contenu ne peut pas s'exécuter. | L'application ne démarre pas, tous les threads de liaison sont occupés et le fournisseur de contenu n'est pas en cours d'exécution. | Réduisez la charge sur les threads de liaison. Autrement dit, effectuez moins d'appels de liaison sortants synchrones ou réduisez vos tâches lors de la gestion des appels entrants. |
Comment déboguer
Pour déboguer les erreurs ANR d'un fournisseur de contenu à l'aide de la signature du groupe et du rapport ANR dans la Google Play Console ou dans Firebase Crashlytics, examinez ce que font le thread principal et le ou les threads de liaison.
L'organigramme suivant explique le débogage d'une erreur ANR de fournisseur de contenu :
L'extrait de code suivant montre à quoi ressemble le thread de liaison lorsqu'il est bloqué en raison d'une requête du fournisseur de contenu lente. Dans ce cas, la requête du fournisseur de contenu attend le verrouillage lors de l'ouverture d'une base de données.
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)
L'extrait de code suivant montre à quoi ressemble le thread principal lorsqu'il est bloqué en raison d'un démarrage lent de l'application. Dans ce cas, le démarrage de l'application est lent en raison d'un conflit de verrouillage lors de l'initialisation de 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)
Réponse de tâche lente
Une erreur ANR de réponse de tâche est lente lorsque l'application met trop de temps à répondre à JobService.onStartJob() ou JobService.onStopJob(), ou met trop de temps à fournir une notification à l'aide de JobService.setNotification(). Cela laisse supposer que le thread principal de l'application est bloqué et qu'il n'effectue pas d'autre action.
En cas de problème avec JobService.onStartJob() ou JobService.onStopJob(), vérifiez ce qui se passe sur le thread principal. En cas de problème avec JobService.setNotification(), veillez à l'appeler aussi rapidement que possible.
Ne faites pas trop de tâches avant d'envoyer la notification.
Afficher le cas de non-respect des règles concernant le regroupement de fils de discussion
Si votre application modifie une vue sur un thread d'arrière-plan, cela peut déclencher une condition de course dans l'état interne de la hiérarchie des vues. Ce non-respect peut empêcher le thread principal (UI) d'exécuter des messages synchrones, ce qui entraîne des problèmes de stabilité critiques :
Cette violation de threading est l'une des causes profondes des traces de pile ANR où le thread principal semble inactif (par exemple, capturé dans "nativePollOnce" ou "thread principal inactif").
Fuite de la barrière de synchronisation
Les vues peuvent être invalidées indirectement en modifiant leur état (par exemple, en appelant TextView.setText ou View.setVisibility) ou directement en appelant View.invalidate ou View.requestLayout. Lorsqu'une vue est invalidée, ViewRootImpl planifie une traversée pour effectuer la mesure, la mise en page et le dessin afin de mettre à jour l'état de l'UI.
L'interface utilisateur se fige alors. Étant donné que MessageQueue ne peut traiter aucun message synchrone après les barrières divulguées, le thread principal passe à l'état inactif. Ce problème se manifeste généralement par une ANR avec nativePollOnce dans la trace de la pile.
Pour éviter les cas de non-respect du threading de vue, suivez ces bonnes pratiques :
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { val data = myRepository.getData() withContext(Dispatchers.Main) { // Switch context to Main myTextView.text = data.title } }
Pour vous aider à suivre les bonnes pratiques, Android propose plusieurs façons d'accéder au thread d'UI à partir d'autres threads :
Les chargeurs d'images populaires, les frameworks d'extensions réactives, les bibliothèques de bus d'événements et d'autres solutions de threading offrent généralement des moyens d'observer certains événements sur le thread principal. Cela est utile pour les observateurs et les écouteurs d'événements qui doivent obtenir et définir l'état des vues.
Déboguer
Android 17 introduit de nouveaux outils pour vous aider à identifier, suivre et résoudre les problèmes de threading de vue pendant le développement.
1. Outils du framework de compatibilité
Les outils du framework de compatibilité permettent aux développeurs d'activer et de désactiver individuellement les modifications de comportement à l'aide des options pour les développeurs ou d'ADB. Pour activer ou désactiver les modifications apportées au comportement en cas de non-respect du threading des vues, procédez comme suit :
Vous pouvez également activer ou désactiver le flag à l'aide d'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'activation de ce signalement force l'application à générer une exception et à planter chaque fois qu'une API View est appelée à partir du mauvais thread. Cela vous aide à détecter et à corriger les problèmes de non-respect du threading de vue.
2. API CalledFromWrongThreadListener
Vous pouvez également implémenter l'API View#registerCalledFromWrongThreadListener globale pour détecter l'accès incorrect aux threads de manière programmatique.
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
Si vous voyez le frame "nativePollOnce" ou "message queue idle" dans les piles ANR, cela signifie souvent que le thread qui ne répondait pas était en réalité inactif et attendait les messages du looper. Dans la Google Play Console, les informations détaillées sur l'ANR se présentent comme suit :
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
Par exemple, si le thread principal est inactif, les piles se présentent comme suit :
"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)
Le thread soupçonné de ne pas répondre peut être inactif pour plusieurs raisons :
Étant donné que les déclencheurs sous-jacents d'une ANR "nativePollOnce" varient selon le type d'ANR, le diagnostic de la catégorie d'ANR spécifique peut aider à identifier les étapes à suivre pour résoudre le problème dans votre application.
| Catégorie | Cause | Correction suggérée |
|---|---|---|
| Délai avant expiration de distribution des entrées | Problème à l'échelle du système Vidage de pile tardif |
Aucune action n'est requise. |
| Aucune fenêtre sélectionnée | Problème au niveau du système Vidage de pile tardif Afficher la violation de threading |
Vérifiez que la base de code ne contient pas d'infractions liées au threading des vues. |
| Délai avant expiration du broadcast receiver | Vidage de pile tardif Problème à l'échelle du système Mauvaise attribution de thread Afficher le non-respect du threading |
Inspectez les threads concernés dans le vidage de pile. Vérifiez que la base de code ne contient pas de cas de non-respect du threading de vue. |
| Délai avant expiration d'exécution du service | Vidage de pile tardif Problème à l'échelle du système Afficher la violation de threading |
Vérifiez que la base de code ne contient pas d'infractions liées au threading des vues. |
| Le fournisseur de contenu ne répond pas | Vidage de pile tardif Problème à l'échelle du système Mauvaise attribution de thread |
Inspectez les threads concernés dans le vidage de pile. |
Voici les étapes recommandées pour analyser les erreurs ANR nativePollOnce.
Aucun bloc de pile
Certains rapports ANR n'incluent pas les piles contenant les erreurs ANR, ce qui signifie que le vidage de la pile a échoué lors de la génération du rapport ANR. L'absence de blocs de pile peut s'expliquer par plusieurs raisons :
[...]
--- 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 -----
[...]
Les erreurs ANR sans bloc de pile ne sont pas exploitables à partir de la signature du groupe ou du rapport ANR. Pour effectuer le débogage, examinez les autres groupes de l'application. En effet, si un problème est suffisamment important, elle dispose généralement de son propre groupe contenant des blocs de pile. Vous pouvez également examiner les traces de Perfetto.
Problèmes connus
Le maintien d'un minuteur dans le processus de votre application afin de terminer le traitement de la diffusion avant le déclenchement d'une erreur ANR peut ne pas fonctionner correctement en raison de la manière asynchrone dont le système surveille les erreurs ANR.