Guides pratiques

Considérations plus approfondies sur les performances

Temps de lecture : 8 min

Reprenez-vous et laissez-nous vous en dire plus sur les performances.

Bienvenue au troisième jour de la semaine consacrée aux performances. Aujourd'hui, nous continuons à partager des informations et des conseils sur des aspects importants des performances des applications. Nous aborderons l'optimisation guidée par le profil, les améliorations des performances de Jetpack Compose et les considérations concernant le travail en arrière-plan. Entrons dans le vif du sujet.

Optimisation guidée par le profil

Les profils de référence et les profils de démarrage sont essentiels pour améliorer les performances de démarrage et d'exécution d'une application Android. Elles font partie d'un groupe d'optimisations des performances appelé "optimisation guidée par le profil".

Lorsqu'une application est empaquetée, le dexer d8 prend les classes et les méthodes, puis remplit les fichiers classes.dex de votre application. Lorsqu'un utilisateur ouvre l'application, ces fichiers dex sont chargés, les uns après les autres, jusqu'à ce que l'application puisse démarrer. En fournissant un profil de démarrage, vous indiquez à d8 les classes et les méthodes à inclure dans les premiers fichiers classes.dex. Cette structure permet à l'application de charger moins de fichiers, ce qui améliore la vitesse de démarrage.

Les profils de référence déplacent efficacement les étapes de compilation Just in Time (JIT) des appareils des utilisateurs vers les machines des développeurs. Le code compilé Ahead Of Time (AOT) généré a prouvé qu'il permettait de réduire à la fois le temps de démarrage et les problèmes de rendu.

Trello et les profils de référence

Nous avons demandé aux ingénieurs de l'application Trello comment les profils de référence avaient affecté les performances de leur application. Après avoir appliqué des profils de référence à son parcours utilisateur principal, Trello a constaté une réduction significative de 25 % du temps de démarrage de l'application.

image.png

Trello a pu améliorer le temps de démarrage de son application de 25 % en utilisant des profils de référence.

Profils de référence chez Meta

Les ingénieurs de Meta ont récemment publié un article sur la façon dont ils accélèrent leurs applications Android avec les profils de référence.

image.png

Dans les applications de Meta, les équipes ont constaté une amélioration de diverses métriques critiques allant jusqu'à 40 % après l'application des profils de référence.

Ces améliorations techniques vous aident également à améliorer la satisfaction des utilisateurs et la réussite de votre entreprise. Partager ces informations avec vos responsables produit, vos directeurs techniques et vos décideurs peut également contribuer à améliorer les performances de votre application.

Premiers pas avec les profils de référence

Pour générer un profil de référence ou de démarrage, vous devez écrire un test macrobenchmark qui exerce l'application. Pendant le test, des données de profil sont collectées et seront utilisées lors de la compilation de l'application. Les tests sont écrits à l'aide de la nouvelle API UiAutomator, que nous aborderons demain.

Écrire un benchmark comme celui-ci est simple. Vous pouvez consulter l'exemple complet sur GitHub.

  @Test

fun profileGenerator() {

    rule.collect(

        packageName = TARGET_PACKAGE,

        maxIterations = 15,

        stableIterations = 3,

        includeInStartupProfile = true

    ) {

        uiAutomator {

            startApp(TARGET_PACKAGE)

        }

    }


}

Points à prendre en compte

Commencez par écrire un profil de référence de test macrobenchmark et un profil de démarrage pour le chemin le plus emprunté par vos utilisateurs. Il s'agit du point d'entrée principal de vos utilisateurs dans votre application, généralement après qu'ils se sont connectés. Écrivez ensuite d'autres scénarios de test pour obtenir une image plus complète, mais uniquement pour les profils de référence. Vous n'avez pas besoin de tout couvrir avec un profil de référence. Limitez-vous aux chemins les plus utilisés et mesurez les performances sur le terrain. Nous y reviendrons dans le post de demain.

Premiers pas avec l'optimisation guidée par profil

Pour en savoir plus sur le fonctionnement des profils de référence, regardez cette vidéo du Sommet des développeurs Android :

Pour en savoir plus, consultez l'épisode Android Build Time sur l'optimisation guidée par le profil :

Nous proposons également des conseils détaillés sur les profils de référence et les profils de démarrage.

Améliorations des performances de Jetpack Compose

Les investissements de l'équipe d'ingénierie dans les performances du framework d'UI pour Android ont porté leurs fruits. Depuis la version 1.9 de Jetpack Compose, les saccades de défilement sont tombées à 0,2 % lors d'un test interne de défilement long. 

jankyFrames.png

Ces améliorations ont été rendues possibles grâce à plusieurs fonctionnalités incluses dans les dernières versions.

Période de cache personnalisable

Par défaut, les mises en page différées ne composent qu'un seul élément à l'avance dans le sens du défilement, et tout ce qui défile hors écran est supprimé. Vous pouvez désormais personnaliser le nombre d'éléments à conserver en indiquant une fraction de la taille de la fenêtre d'affichage ou une taille en dp. Cela permet à votre application d'effectuer plus de travail en amont et d'utiliser le temps disponible plus efficacement après l'activation de la composition pouvant être mise en pause entre les frames.

Pour commencer à utiliser des fenêtres de cache personnalisables, instanciez un LazyLayoutCacheWindow et transmettez-le à votre liste ou grille lazy. Mesurez les performances de votre application à l'aide de différentes tailles de fenêtre de cache, par exemple 50 % de la fenêtre d'affichage. La valeur optimale dépendra de la structure de votre contenu et de la taille des éléments.

  val dpCacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)

val state = rememberLazyListState(cacheWindow = dpCacheWindow)

LazyColumn(state = state) {

    // column contents

}

Composition pouvant être mise en pause

Cette fonctionnalité permet de mettre en pause les compositions et de répartir leur travail sur plusieurs frames. Les API ont été intégrées à la version 1.9 et sont désormais utilisées par défaut dans la version 1.10 pour la prélecture de la mise en page différée. Vous devriez constater les plus grands avantages avec les éléments complexes dont la durée de composition est plus longue. 

image.png

Autres optimisations des performances de Compose

Dans les versions 1.9 et 1.10 de Compose, l'équipe a également apporté plusieurs optimisations un peu moins évidentes.

Plusieurs API qui utilisent des coroutines en arrière-plan ont été améliorées. Par exemple, lorsqu'ils utilisent Draggable et Clickable, les développeurs devraient constater des temps de réaction plus rapides et une amélioration du nombre d'allocations.

L'optimisation du suivi des rectangles de mise en page a amélioré les performances des modificateurs tels que onVisibilityChanged() et onLayoutRectChanged(). Cela accélère la phase de mise en page, même lorsque vous n'utilisez pas explicitement ces API.

L'utilisation de valeurs mises en cache lors de l'observation des positions via onPlaced() permet également d'améliorer les performances.

Prérécupérer le texte en arrière-plan

À partir de la version 1.9, Compose permet de précharger du texte sur un thread d'arrière-plan. Cela vous permet de préchauffer les caches pour accélérer la mise en page du texte. Cette fonctionnalité est pertinente pour les performances de rendu des applications. Lors de la mise en page, le texte doit être transmis au framework Android, où un cache de mots est rempli. Par défaut, cette méthode s'exécute sur le thread UI. Le déchargement de la prélecture et le remplissage du cache de mots sur un thread en arrière-plan peuvent accélérer la mise en page, en particulier pour les textes plus longs. Pour précharger sur un thread d'arrière-plan, vous pouvez transmettre un exécuteur personnalisé à n'importe quel composable qui utilise BasicText en arrière-plan en transmettant un LocalBackgroundTextMeasurementExecutor à un CompositionLocalProvider comme suit.

  val defaultTextMeasurementExecutor = Executors.newSingleThreadExecutor()

CompositionLocalProvider(

    LocalBackgroundTextMeasurementExecutor provides DefaultTextMeasurementExecutor

) {

    BasicText("Some text that should be measured on a background thread!")


}

En fonction du texte, cela peut améliorer les performances de l'affichage du texte. Pour vous assurer qu'il améliore les performances d'affichage de votre application, comparez les résultats.

Considérations sur les performances des tâches en arrière-plan

Le travail en arrière-plan est un élément essentiel de nombreuses applications. Vous utilisez peut-être des bibliothèques comme WorkManager ou JobScheduler pour effectuer des tâches telles que :

  • Importer régulièrement des événements analytiques
  • Synchroniser des données entre un service de backend et une base de données
  • Traitement des contenus multimédias (redimensionnement ou compression d'images, par exemple)

L'un des principaux défis lors de l'exécution de ces tâches consiste à trouver un équilibre entre performances et efficacité énergétique. WorkManager vous permet d'atteindre cet équilibre. Il est conçu pour être économe en énergie et permettre de différer le travail vers une fenêtre d'exécution optimale influencée par un certain nombre de facteurs, y compris les contraintes que vous spécifiez ou celles imposées par le système. 

Toutefois, WorkManager n'est pas une solution universelle. Android propose également un certain nombre d'API optimisées pour l'alimentation, qui sont conçues spécifiquement pour certains parcours utilisateur clés (CUJ) courants.  

Consultez la page de destination sur le travail en arrière-plan pour obtenir une liste de quelques-unes de ces tâches,  y compris la mise à jour d'un widget et l'obtention de la position en arrière-plan.

Outils de débogage local pour le travail en arrière-plan : scénarios courants

Pour déboguer le travail en arrière-plan et comprendre pourquoi une tâche a pu être retardée ou a échoué, vous devez savoir comment le système a planifié vos tâches. 

Pour vous aider, WorkManager propose plusieurs outils associés pour vous aider à déboguer localement et à optimiser les performances (certains d'entre eux fonctionnent également pour JobScheduler). Voici quelques scénarios courants que vous pouvez rencontrer lorsque vous utilisez WorkManager, ainsi qu'une explication des outils que vous pouvez utiliser pour les déboguer.

Déboguer l'exécution du travail planifié

Le retard ou l'absence d'exécution des tâches planifiées peuvent être dus à plusieurs facteurs, y compris au non-respect des contraintes spécifiées ou à des contraintes imposées par le système

La première étape pour déterminer pourquoi le travail planifié ne s'exécute pas consiste à confirmer qu'il a bien été planifié.  Après avoir confirmé l'état de la planification, déterminez s'il existe des contraintes ou des conditions préalables non respectées qui empêchent l'exécution du travail.

Plusieurs outils permettent de déboguer ce scénario.

Background Task Inspector

L'outil Background Task Inspector est un outil puissant intégré directement à Android Studio. Il fournit une représentation visuelle de toutes les tâches WorkManager et de leurs états associés (en cours d'exécution, en file d'attente, échec, réussite). 

Pour comprendre pourquoi le travail planifié ne s'exécute pas avec Background Task Inspector, consultez le ou les états de travail listés. L'état "En file d'attente" indique que votre tâche a été planifiée, mais qu'elle n'a pas encore été exécutée.

Avantages : en plus de vous permettre de consulter facilement toutes les tâches, cet outil est particulièrement utile si vous avez des tâches enchaînées. L'inspecteur de tâches en arrière-plan offre une vue graphique qui permet de visualiser si l'échec d'une tâche précédente a pu avoir un impact sur l'exécution de la tâche suivante.

image.png

Vue Liste de Background Task Inspector

image.png

Vue graphique de Background Task Inspector

adb shell dumpsys jobscheduler

Cette commande renvoie la liste de tous les jobs JobScheduler actifs (y compris les Workers WorkManager) avec les contraintes spécifiées et celles imposées par le système. Il renvoie également l'historique des jobs. 

Utilisez cette option si vous souhaitez afficher vos tâches planifiées et les contraintes associées d'une autre manière. Pour les versions de WorkManager antérieures à la version 2.10.0, adb shell dumpsys jobscheduler renvoie une liste de Workers portant ce nom :

  [package name]/androidx.work.impl.background.systemjob.SystemJobService

Si votre application comporte plusieurs workers, la mise à jour vers WorkManager 2.10.0 vous permettra de voir les noms des workers et de les distinguer facilement :

  #WorkerName#@[package name]/androidx.work.impl.background.systemjob.SystemJobService

Avantages  : cette commande est utile pour comprendre s'il y a eu des contraintes imposées par le système , que vous ne pouvez pas déterminer avec l'inspecteur de tâches en arrière-plan. Par exemple, cela renverra le bucket de veille de votre application, ce qui peut avoir une incidence sur la fenêtre dans laquelle le travail planifié se termine.

Activer la journalisation du débogage

Vous pouvez activer la journalisation personnalisée pour afficher les journaux WorkManager détaillés, auxquels WM— sera associé. 

Avantages : vous pouvez ainsi savoir quand le travail est planifié, quand les contraintes sont respectées et quand les événements de cycle de vie se produisent. Vous pouvez consulter ces journaux lors du développement de votre application.

WorkInfo.StopReason

Si vous constatez des performances imprévisibles avec un nœud de calcul spécifique, vous pouvez observer de manière programmatique la raison pour laquelle votre nœud de calcul a été arrêté lors de la tentative d'exécution précédente avec WorkInfo.getStopReason

Nous vous recommandons de configurer votre application pour qu'elle observe WorkInfo à l'aide de getWorkInfoByIdFlow afin d'identifier si votre tâche est affectée par des restrictions en arrière-plan, des contraintes, des délais d'attente fréquents ou même si elle a été arrêtée par l'utilisateur.

Avantages : vous pouvez utiliser WorkInfo.StopReason pour collecter des données de champ sur les performances de vos employés.

Déboguer la longue durée de wakelock attribuée à WorkManager signalée par Android Vitals

Android Vitals inclut une métrique sur le nombre de wakelocks partiels excessifs, qui met en évidence les wakelocks contribuant à la décharge de la batterie. Vous serez peut-être surpris d'apprendre que WorkManager acquiert des wake locks pour exécuter des tâches. Si ces wake locks dépassent le seuil défini par Google Play, cela peut avoir un impact sur la visibilité de votre application. Comment déboguer la durée de verrouillage de l'écran attribuée à votre application ? Vous pouvez utiliser les outils suivants.

Tableau de bord Android Vitals

Commencez par vérifier dans le tableau de bord Vitals Android sur la durée excessive de verrouillage de l'écran que la durée élevée de verrouillage de l'écran provient de WorkManager et non d'une alarme ou d'un autre verrouillage de l'écran. Vous pouvez consulter la documentation Identifier les wakelocks créés par d'autres API pour comprendre quels wakelocks sont détenus par WorkManager. 

Perfetto

Perfetto est un outil permettant d'analyser les traces système. Lorsque vous l'utilisez pour déboguer WorkManager en particulier, vous pouvez consulter la section "État de l'appareil" pour voir quand votre tâche a commencé, combien de temps elle s'est exécutée et comment elle contribue à la consommation d'énergie. 

Dans la section "État de l'appareil : tâches", vous pouvez voir tous les workers qui ont été exécutés et leurs verrous de réveil associés.

deviceState.png

Section "État de l'appareil" dans Perfetto, montrant l'exécution de CleanupWorker et BlurWorker.

Ressources

Consultez la page Déboguer WorkManager pour obtenir une présentation des méthodes de débogage disponibles pour d'autres scénarios que vous pourriez rencontrer.

Pour essayer certaines de ces méthodes et en savoir plus sur le débogage de WorkManager, consultez l'atelier de programmation WorkManager et tests : niveau avancé.

Étapes suivantes

Aujourd'hui, nous avons dépassé la réduction de code et exploré la façon dont Android Runtime et Jetpack Compose rendent réellement votre application. Qu'il s'agisse de précompiler des chemins critiques avec des profils de référence ou de lisser les états de défilement avec les nouvelles fonctionnalités de Compose 1.9 et 1.10, ces outils se concentrent sur l'expérience utilisateur de votre application. Nous avons également approfondi les bonnes pratiques pour le débogage des tâches en arrière-plan.

Demander à Android

Vendredi, nous organisons une session de questions-réponses en direct sur les performances. Posez vos questions dès maintenant avec le hashtag #AskAndroid et obtenez des réponses d'experts.

Le défi

Lundi, nous vous avons demandé d'activer R8. Aujourd'hui, nous vous demandons de générer un profil de référence pour votre application.

Avec Android Studio Otter, l'assistant du module du générateur de profils de référence facilite plus que jamais cette tâche. Choisissez votre parcours utilisateur le plus critique (même s'il s'agit simplement du démarrage et de la connexion à votre application), puis générez un profil.

Une fois que vous l'avez, exécutez un Macrobenchmark pour comparer CompilationMode.None à CompilationMode.Partial.

Partagez vos améliorations du temps de démarrage sur les réseaux sociaux en utilisant le hashtag #optimizationEnabled.

Rendez-vous demain

Vous avez réduit la taille de votre application avec R8 et optimisé votre environnement d'exécution avec l'optimisation guidée par le profil. Mais comment prouver ces réussites à vos parties prenantes ? Comment détecter les régressions avant qu'elles n'affectent la production ?

Rejoignez-nous demain pour le quatrième jour : le guide de nivellement des performances. Nous vous expliquerons exactement comment mesurer votre succès, des données de terrain dans Vitals Play au traçage local approfondi avec Perfetto.

Écrit par :

Lire la suite