Ce document vous aide à identifier et à résoudre les principaux problèmes de performances dans votre application.
Principaux problèmes de performances
De nombreux problèmes peuvent nuire aux performances d'une application, mais voici les plus courants :
- Défilement par à-coups
À-coups est le terme qui décrit le problème visuel qui se produit lorsque le système n'est pas en mesure de construire et de fournir des frames à temps pour qu'ils soient affichés à l'écran à la cadence demandée de 60 Hz ou plus. Les à-coups sont particulièrement visibles en cas de défilement, alors que le flux d'animation doit être fluide. Des à-coups apparaissent lorsque le mouvement s'interrompt pendant la lecture d'un ou de plusieurs frames, car l'affichage du contenu de l'application prend plus de temps que la durée d'un frame sur le système.
Les applications doivent cibler des fréquences d'actualisation de 90 Hz. Les fréquences de rendu traditionnelles sont de 60 Hz, mais de nombreux appareils plus récents fonctionnent en mode 90 Hz lors d'interactions utilisateur telles que le défilement. Certains appareils acceptent des fréquences encore plus élevées, jusqu'à 120 Hz.
Pour connaître la fréquence d'actualisation utilisée par un appareil à un moment donné, activez une superposition en sélectionnant Options pour les développeurs > Voir la fréquence d'actualisation dans la section Débogage.
- Latence de démarrage
La latence de démarrage désigne le temps écoulé entre l'appui sur l'icône d'une application, une notification ou un autre point d'entrée, et l'affichage des données de l'utilisateur à l'écran.
Visez les objectifs de démarrage suivants dans vos applications :
Démarrage à froid en moins de 500 ms. Un démarrage à froid se produit lorsque l'application en cours de lancement n'est pas présente dans la mémoire du système. Cela se produit lorsqu'il s'agit du premier lancement de l'application depuis le redémarrage ou lorsque le processus de l'application est arrêté par l'utilisateur ou le système.
En revanche, un démarrage tiède se produit lorsque l'application s'exécute déjà en arrière-plan. Un démarrage à froid nécessite le plus de travail du système, car il doit tout charger à partir du stockage et initialiser l'application. Essayez d'effectuer des démarrages à froid en 500 ms au maximum.
Des latences P95 et P99 très proches de la latence médiane. Lorsque l'application met beaucoup de temps à démarrer, l'expérience utilisateur est médiocre. Les communications inter-processus (IPC) et les E/S inutiles lors du processus critique de démarrage d'une application peuvent subir des conflits de verrouillage et introduire des incohérences.
- Transitions manquant de fluidité
Ces problèmes surviennent lors des interactions, par exemple lorsque vous passez d'un onglet à l'autre ou chargez une nouvelle activité. Ces types de transitions doivent être des animations fluides et ne pas inclure de retards ni de scintillements visuels.
- Inefficacité énergétique
Toute tâche consomme de la batterie et les tâches inutiles réduisent l'autonomie de la batterie.
Les allocations de mémoire, qui proviennent de la création d'objets dans le code, peuvent entraîner un travail important dans le système. En effet, non seulement les allocations elles-mêmes nécessitent un effort de la part de l'environnement d'exécution Android Runtime (ART), mais la libération de ces objets ultérieurement (récupération de mémoire) nécessite également du temps et des efforts. L'allocation et la collecte sont bien plus rapides et efficaces, en particulier pour les objets temporaires. Auparavant, il était recommandé d'éviter d'allouer des objets dans la mesure du possible. Toutefois, nous vous recommandons de faire ce qui convient le mieux à votre application et à votre architecture. Économiser sur les allocations au risque que le code devienne ingérable n'est pas le choix idéal, étant donné les capacités de l'ART.
Cependant, cela demande un certain effort. Gardez donc en tête que l'allocation de nombreux objets dans votre boucle interne pourrait entraîner des problèmes de performances.
Identifier les problèmes
Nous vous recommandons de suivre le workflow ci-dessous pour identifier et résoudre les problèmes de performances :
- Identifiez et inspectez les critical user journeys suivants :
- Les flux de démarrage courants, y compris depuis le lanceur d'applications et les notifications.
- Les écrans sur lesquels l'utilisateur fait défiler les données.
- Les transitions entre les écrans.
- Les flux de longue durée, comme la navigation ou la lecture de musique.
- Inspectez ce qui se passe au cours des flux précédents à l'aide des outils de débogage suivants :
- Perfetto : permet de voir ce qui se passe sur l'ensemble de l'appareil, à l'aide de données temporelles précises.
- Profileur de mémoire : permet de voir quelles allocations de mémoire se produisent sur le tas de mémoire.
- Simpleperf : affiche un FlameGraph qui identifie les appels de fonction qui utilisent le plus le processeur au cours d'une période donnée. Si vous identifiez un élément qui prend beaucoup de temps dans Systrace, mais que vous ne savez pas pourquoi, Simpleperf peut fournir des informations supplémentaires.
Pour comprendre et déboguer ces problèmes de performances, il est essentiel de déboguer manuellement chaque exécution de test. Vous ne pouvez pas remplacer les étapes précédentes par une analyse de données agrégées. Toutefois, pour comprendre ce que les utilisateurs voient réellement et identifier quand des régressions peuvent se produire, il est important de configurer la collecte de métriques dans des tests automatisés et dans le champ :
- Flux de démarrage
- Métriques de champ : temps de démarrage de la Play Console
- Tests labo : test du démarrage avec Macrobenchmark
- À-coups
- Métriques de champ
- Données essentielles liées aux frames de la Play Console : il n'est pas possible de limiter les métriques à un parcours utilisateur spécifique depuis la Play Console. Seuls les à-coups globaux dans l'application sont signalés.
- Mesure personnalisée avec
FrameMetricsAggregator
: vous pouvez utiliserFrameMetricsAggregator
pour enregistrer les métriques d'à-coups pendant un workflow particulier.
- Tests labo
- Défilement avec Macrobenchmark.
- Macrobenchmark collecte le temps de rendu à l'aide de commandes
dumpsys gfxinfo
qui regroupent un seul parcours utilisateur. Il s'agit d'un moyen de comprendre les variations d'à-coups sur un parcours utilisateur spécifique. Les métriquesRenderTime
, qui mettent en évidence le temps nécessaire pour dessiner les frames, sont plus importantes que le nombre de frames saccadés pour identifier les régressions ou les améliorations.
- Métriques de champ
Configurer votre application pour l'analyse des performances
Une configuration appropriée est essentielle pour obtenir des analyses comparatives précises, reproductibles et exploitables à partir d'une application. Effectuez des tests sur un système le plus proche possible de la production, tout en supprimant les sources de bruit. Les sections suivantes présentent plusieurs étapes spécifiques à l'APK et au système que vous pouvez suivre pour préparer une configuration de test. Certaines d'entre elles sont spécifiques à un cas d'utilisation.
Points de trace
Les applications peuvent instrumenter leur code avec des événements de trace personnalisés.
Lors de la capture des traces, le traçage implique une légère surcharge d'environ 5 μs par section. Par conséquent, ne l'utilisez pas pour toutes les méthodes. Le traçage de segments de tâche plus larges (> 0,1 ms) peut fournir des informations importantes sur les goulots d'étranglement.
Remarques sur l'APK
Les variantes de débogage peuvent être utiles pour résoudre les problèmes et symboliser les échantillons de piles, mais elles ont un impact non linéaire majeur sur les performances. Les appareils équipés d'Android 10 (niveau d'API 29) ou version ultérieure peuvent utiliser profileable
android:shell="true"
dans leur fichier manifeste pour activer le profilage dans les builds.
Utilisez votre configuration de minification du code de niveau production. Selon les ressources utilisées par votre application, cela peut avoir un impact important sur les performances. Certaines configurations ProGuard suppriment les points de trace. Par conséquent, pensez à supprimer ces règles pour la configuration sur laquelle vous exécutez des tests.
Compilation
Compilez votre application sur l'appareil dans un état connu, généralement speed
ou speed-profile
.
L'activité juste à temps (JIT) en arrière-plan peut avoir un impact important sur les performances, et vous l'atteignez souvent si vous réinstallez l'APK entre deux exécutions de test. Pour ce faire, exécutez la commande suivante :
adb shell cmd package compile -m speed -f com.google.packagename
Le mode de compilation speed
compile complètement l'application. Le mode speed-profile
compile l'application en fonction d'un profil des chemins de code utilisés qui sont collectés lors de l'utilisation de l'application. Il peut être difficile de collecter des profils de manière cohérente et correcte. Par conséquent, si vous décidez de les utiliser, vérifiez qu'ils collectent les éléments attendus. Les profils se trouvent à l'emplacement suivant :
/data/misc/profiles/ref/[package-name]/primary.prof
Macrobenchmark vous permet de spécifier directement un mode de compilation.
Remarques concernant le système
Pour les mesures de bas niveau et de haute fidélité, calibrez vos appareils. Effectuez des comparaisons A/B sur le même appareil et la même version d'OS. Il peut exister des variations importantes des performances, même sur le même type d'appareil.
Sur les appareils en mode root, envisagez d'utiliser un script lockClocks
pour les microbenchmarks. Ces scripts effectuent notamment les opérations suivantes :
- Réglage des processeurs à une fréquence fixe
- Désactivation des petits cœurs et configuration du GPU
- Désactivation de la limitation thermique
Nous vous déconseillons d'utiliser un script lockClocks
pour les tests axés sur l'expérience utilisateur, tels que le lancement d'une application, les tests DoU et les tests d'à-coups. Toutefois, cela peut s'avérer essentiel pour réduire le bruit dans les tests Microbenchmark.
Dans la mesure du possible, envisagez d'utiliser un framework de test tel que Macrobenchmark, qui permet de réduire le bruit et d'éviter les erreurs de mesure.
Démarrage lent de l'application : activité de trampoline inutile
Une activité de trampoline peut prolonger inutilement le démarrage d'une application. Il est important de savoir si votre application est concernée. Comme le montre l'exemple de trace suivant, un événement activityStart
est immédiatement suivi d'un autre événement activityStart
sans qu'aucun frame ne soit dessiné par la première activité.
Figure 1. Trace montrant une activité de trampoline
Cela peut se produire à la fois dans un point d'entrée de notification et dans un point d'entrée de démarrage d'application standard, et le problème peut souvent être résolu par la refactorisation. Par exemple, si vous utilisez cette activité pour effectuer la configuration avant l'exécution d'une autre activité, intégrez ce code dans un composant ou une bibliothèque réutilisables.
Les allocations inutiles déclenchent des récupérations de mémoire fréquentes
Vous remarquerez peut-être que les récupérations de mémoire se produisent plus fréquemment que prévu dans une trace Systrace.
Dans l'exemple suivant, toutes les 10 secondes au cours d'une opération de longue durée, cela indique que votre application alloue inutilement et de manière cohérente au fil du temps :
Figure 2. Trace montrant l'espace entre les événements de récupération de mémoire
Vous remarquerez peut-être également qu'une pile d'appel spécifique effectue la grande majorité des allocations lorsque vous utilisez le Profileur de mémoire. Vous n'avez pas besoin d'éliminer toutes les allocations de manière agressive, car cela peut rendre le code plus difficile à gérer. Commencez plutôt par travailler sur les hotspots des allocations.
Frames saccadés
Le pipeline graphique est relativement complexe, et il peut être difficile de déterminer si un utilisateur a finalement subi une perte de fréquence de frames. Dans certains cas, la plate-forme peut "sauver" un frame grâce à la mise en mémoire tampon. Cependant, vous pouvez ignorer la plupart de ces nuances pour identifier les frames problématiques du point de vue de votre application.
Lorsque les frames sont dessinés avec un effort minimal de l'application, les points de trace Choreographer.doFrame()
se produisent à une cadence de 16,7 ms sur un appareil de 60 FPS :
Figure 3. Trace affichant des frames rapides fréquents
Si vous faites un zoom arrière et que vous parcourez la trace, vous verrez parfois que les frames prennent un peu plus de temps, mais cela ne pose pas de problème, car ils ne prennent pas plus de 16,7 ms.
Figure 4. Trace affichant des frames rapides fréquents associés à des pics de travail périodiques
Lorsque vous constatez une interruption de cette cadence régulière, il s'agit d'un frame saccadé, comme illustré dans la figure 5 :
Figure 5. Trace montrant un frame saccadé
Vous pouvez vous entraîner à les identifier.
Figure 6. Trace affichant plus de frames saccadés
Dans certains cas, vous devrez effectuer un zoom avant sur un point de trace pour en savoir plus sur les vues gonflées ou sur ce que fait RecyclerView
. Dans d'autres cas, vous devrez peut-être procéder à une inspection plus approfondie.
Pour en savoir plus sur l'identification des frames saccadés et le débogage de leurs causes, consultez Affichage lent.
Erreurs RecyclerView courantes
L'invalidation inutile de l'intégralité des données de sauvegarde de RecyclerView
peut entraîner de longs délais d'affichage des frames et des à-coups. Pour réduire le nombre de vues à mettre à jour, n'invalidez que les données modifiées.
Consultez Présenter des données dynamiques pour éviter les appels notifyDatasetChanged()
coûteux qui entraînent la mise à jour du contenu au lieu de le remplacer complètement.
Si vous ne prenez pas correctement en charge chaque RecyclerView
imbriqué, le RecyclerView
interne peut être entièrement recréé à chaque fois. Chaque RecyclerView
imbriqué interne doit disposer d'un RecycledViewPool
défini pour garantir que les vues peuvent être recyclées entre chaque RecyclerView
interne.
Si la récupération préalable de données est insuffisante ou non opportune, il peut être pénible d'atteindre le bas d'une liste déroulante lorsqu'un utilisateur doit attendre plus de données du serveur. Bien que cela ne soit pas techniquement un "à-coup", car aucune date limite d'affichage n'est ratée, vous pouvez améliorer considérablement l'expérience utilisateur pour modifier le délai et la quantité de préchargement afin que l'utilisateur ne doive pas attendre les données.
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé.
- Analyse et optimisation du démarrage de l'application {:#app-startup-analysis-optimization}
- Cadres figés
- Écrire un macrobenchmark