Temps de démarrage de l'application

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Les utilisateurs s'attendent à ce que les applications soient réactives et se chargent rapidement. Une application dont le démarrage est lent ne répond pas à cette attente et peut être décevante pour les utilisateurs. Cette mauvaise expérience peut conduire l'utilisateur à donner une mauvaise note à votre application sur le Play Store, voire à l'abandonner complètement.

Les informations contenues dans ce document vous permettent d'optimiser le temps de démarrage de votre application. Ce document commence par expliquer les principes internes du processus de démarrage. Ensuite, il explique comment profiler les performances de démarrage. Enfin, il décrit certains problèmes courants liés au démarrage et donne des conseils pour les résoudre.

Comprendre les différents états de démarrage de l'application

Le démarrage d'une application peut se produire dans l'un des trois états suivants, chacun d'entre eux ayant une incidence sur le délai nécessaire pour que votre application soit visible par l'utilisateur : démarrage à froid, démarrage tiède ou démarrage à chaud. À froid, votre application part de zéro. Dans les autres états, le système doit faire passer l'application qui est en cours d'exécution en arrière-plan au premier plan. Nous vous recommandons de procéder à une optimisation en partant systématiquement du principe qu'il y a un démarrage à froid. Cela peut également améliorer les performances des démarrages tiède et à chaud.

Pour optimiser le démarrage rapide de votre application, il est utile de comprendre ce qui se passe au niveau des systèmes et des applications, ainsi que leurs interactions dans chacun de ces états.

Démarrage à froid

Le terme "démarrage à froid" désigne le démarrage d'une application à partir de zéro : le processus du système n'a pas créé le processus de l'application avant ce démarrage. Les démarrages à froid se produisent dans les cas où l'application est lancée pour la première fois depuis le démarrage de l'appareil ou depuis la fermeture de l'application par le système. Ce type de démarrage présente la plus grande difficulté en termes de réduction du temps de démarrage, car il demande plus de travail au système et à l'application que les autres états de démarrage.

Au début d'un démarrage à froid, le système doit effectuer trois tâches. Ces tâches sont les suivantes :

  1. Chargement et lancement de l'application
  2. Affichage d'une fenêtre de démarrage vide pour l'application immédiatement après le démarrage
  3. Création du processus d'application

Dès que le système a créé le processus d'application, ce processus est responsable des étapes suivantes :

  1. Création de l'objet d'application
  2. Lancement du thread principal
  3. Création de l'activité principale
  4. Gonflage des vues
  5. Mise en page de l'écran
  6. Réalisation de la visualisation initiale

Une fois que le processus d'application a effectué la première visualisation, le processus système remplace la fenêtre d'arrière-plan actuellement affichée par l'activité principale. L'utilisateur peut alors commencer à utiliser l'application.

La figure 1 montre comment les processus du système et de l'application se passent la main.

Figure 1 : Représentation visuelle des principaux éléments du démarrage à froid d'une application.

Des problèmes de performances peuvent être rencontrés lors de la création de l'application et de l'activité.

Création de l'application

Lorsque l'application est lancée, la fenêtre de démarrage vide reste affichée jusqu'à ce que le système ait fini d'afficher l'application. À ce stade, le processus système remplace la fenêtre de démarrage de votre application, ce qui permet à l'utilisateur de commencer à interagir avec cette dernière.

Si vous avez ignoré Application.onCreate() dans votre application, le système appelle la méthode onCreate() sur votre objet d'application. Par la suite, l'application génère le thread principal, également appelé "thread UI", et le charge de créer votre activité principale.

À ce stade, les processus au niveau du système et de l'application se déroulent conformément aux étapes du cycle de vie de l'application.

Création d'activité

Une fois que le processus de l'application a créé votre activité, elle effectue les opérations suivantes :

  1. Initialisation des valeurs
  2. Appel des constructeurs
  3. Appel à la méthode de rappel, telle que Activity.onCreate(), adaptée à l'état du cycle de vie actuel de l'activité

En général, la méthode onCreate() a l'impact le plus élevé sur le temps de chargement, car elle effectue les tâches dont la surcharge est la plus élevée : charger et gonfler les vues, et initialiser les objets nécessaires à l'exécution de l'activité.

Démarrage tiède

Un démarrage tiède englobe certains sous-ensembles d'opérations qui ont lieu lors d'un démarrage à froid. Ce démarrage occasionne plus de surcharge qu'un démarrage à chaud. De nombreux états potentiels peuvent être considérés comme des démarrages tièdes. Par exemple :

  • L'utilisateur quitte votre appli, puis la redémarre. Le processus peut continuer à s'exécuter, mais l'application doit recréer l'activité à partir de zéro via un appel à onCreate().

  • Le système évince votre application de la mémoire, puis l'utilisateur la relance. Le processus et l'activité doivent être redémarrés, mais la tâche peut bénéficier du groupe d'états enregistrés d'instances transmis dans onCreate().

Démarrage à chaud

Le démarrage à chaud de votre application est beaucoup plus simple et moins coûteux qu'un démarrage à froid. Lors d'un démarrage à chaud, le système passe votre activité au premier plan. Si toutes les activités de votre application sont toujours en mémoire, l'application n'a pas besoin de recommencer l'initialisation des objets, le gonflage de la mise en page et l'affichage.

Toutefois, si une partie de la mémoire a été définitivement vidée à la suite d'événements de nettoyage, tels que onTrimMemory(), ces objets doivent être recréés en réponse au démarrage à chaud.

Le démarrage à chaud présente le même comportement à l'écran que le démarrage à froid :

Le processus système affiche un écran vide jusqu'à ce que l'application ait fini d'afficher l'activité.

Figure 2 : Ce schéma montre les différents états de démarrage et leurs processus respectifs, chaque état commençant à partir de la première image.

Utiliser les métriques pour détecter et diagnostiquer les problèmes

Pour diagnostiquer correctement les problèmes de performances liées au temps de démarrage, vous pouvez suivre les métriques qui indiquent le temps nécessaire au démarrage de votre application. Android vous informe que votre application rencontre un problème et vous aide à le diagnostiquer de plusieurs manières. Android Vitals peut vous avertir que le problème se produit, et les outils de diagnostic peuvent vous aider à le diagnostiquer.

Avantages des métriques de démarrage

Android utilise les métriques Délai d'affichage initial et Délai d'affichage total pour optimiser le démarrage à froid et tiède des applications. Android Runtime (ART) utilise les données de ces métriques pour précompiler efficacement le code afin d'optimiser les démarrages ultérieurs.

Des démarrages plus rapides entraînent une interaction plus durable des utilisateurs avec votre application, ce qui réduit le nombre d'instances de sortie anticipée, de redémarrage ou de navigation vers une autre application.

Android Vitals

Android Vitals peut vous aider à améliorer les performances de votre application en vous alertant via la Play Console lorsque les temps de démarrage de votre application sont trop longs. Android Vitals considère que le temps de démarrage de votre application est excessif si :

  • le démarrage à froid prend cinq secondes ou plus ;
  • le démarrage tiède prend deux secondes ou plus ;
  • le démarrage à chaud prend 1,5 seconde ou plus.

Android Vitals utilise la métrique Délai d'affichage initial. Pour savoir comment Google Play collecte les données Android Vitals, consultez la documentation de la Play Console.

Délai d'affichage initial

Le délai d'affichage initial mesure le temps nécessaire pour qu'une application produise sa première image, y compris l'initialisation du processus (s'il s'agit d'un démarrage à froid), la création d'activités (démarrage à froid/tiède) et l'affichage de la première image.

Comment récupérer le délai d'affichage initial

Dans Android 4.4 (niveau d'API 19) ou version ultérieure, logcat inclut une ligne de sortie contenant une valeur appelée Displayed. Cette valeur représente le temps écoulé entre le lancement du processus et l'affichage de l'activité correspondante à l'écran. Le temps écoulé comprend la séquence d'événements suivante :

  • Lancement du processus
  • Initialisation des objets
  • Création et initialisation de l'activité
  • Gonflage de la mise en page
  • Visualisation de votre application pour la première fois

La ligne de journal indiquée ressemble à l'exemple suivant :

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

Si vous suivez la sortie logcat à partir de la ligne de commande ou dans un terminal, il est facile de trouver le temps écoulé. Pour connaître le temps écoulé dans Android Studio, vous devez désactiver les filtres dans votre vue logcat. Cette opération est nécessaire, car c'est le serveur système, et non l'application elle-même, qui gère ce journal.

Une fois que vous avez défini les paramètres appropriés, il vous suffit de rechercher le terme approprié pour afficher le temps écoulé. La figure 2 montre comment désactiver les filtres et, dans la deuxième ligne de résultat à partir du bas, un exemple de sortie logcat du temps Displayed.

Figure 2 : Désactivation des filtres et recherche de la valeur "Displayed" dans logcat.

La métrique Displayed dans la sortie logcat ne capture pas nécessairement le temps nécessaire avant que toutes les ressources ne soient chargées et affichées : elle exclut les ressources qui ne sont pas référencées dans le fichier de mise en page ou que l'application crée dans le cadre de l'initialisation de l'objet. Elle exclut ces ressources, car leur chargement est un processus intégré et ne bloque pas l'affichage initial de l'application.

La ligne Displayed de la sortie logcat contient parfois un champ supplémentaire indiquant la durée totale. Exemple :

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

Dans ce cas, la première mesure ne concerne que l'activité qui a été visualisée pour la première fois. La mesure du temps total commence au début du processus de l'application et peut inclure une autre activité qui a commencé en premier, mais qui n'a rien affiché à l'écran. La mesure du temps total n'est affichée que s'il existe une différence entre l'activité unique et le temps total de démarrage.

Vous pouvez également mesurer le délai d'affichage initial en exécutant votre application avec la commande ADB Shell Activity Manager. Exemple :

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN</pre>

La métrique Displayed apparaît dans la sortie logcat comme précédemment. Votre fenêtre de terminal doit également afficher les éléments suivants :

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

Les arguments -c et -a sont facultatifs et vous permettent de spécifier une <category> et une <action>.

Délai d'affichage total

La métrique "Délai d'affichage total" mesure le temps nécessaire à l'application pour produire sa première image au contenu complet, y compris le contenu chargé de manière asynchrone après la première image. Il s'agit généralement du contenu de la liste principale chargée depuis le réseau, comme indiqué par l'application.

Comment récupérer le délai d'affichage total

Vous pouvez utiliser la méthode reportFullyDrawn() pour mesurer le temps écoulé entre le lancement de l'application et l'affichage complet de toutes les ressources et les hiérarchies de vues. Cela peut être utile lorsqu'une application effectue un chargement différé. Avec un chargement différé, une application ne bloque pas l'affichage initial de la fenêtre, mais charge les ressources de manière asynchrone et met à jour la hiérarchie des vues.

Si, en raison du chargement différé, l'affichage initial d'une application n'inclut pas toutes les ressources, vous pouvez envisager de charger et d'afficher toutes les ressources et vues comme une métrique distincte. Par exemple, votre interface utilisateur peut être entièrement chargée avec du texte, sans pour le moment afficher d'images que l'application doit extraire du réseau.

Pour résoudre ce problème, vous pouvez appeler manuellement reportFullyDrawn() pour indiquer au système que le chargement différé est terminé. Lorsque vous utilisez cette méthode, la valeur affichée par logcat correspond au temps écoulé entre la création de l'objet d'application et le moment où reportFullyDrawn() est appelé. Voici un exemple de résultat logcat :

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

La sortie logcat inclut parfois un temps total, comme indiqué dans la section Délai d'affichage initial.

Si vous découvrez que les temps d'affichage sont plus lents que ce que vous souhaiteriez, vous pouvez essayer d'identifier les goulots d'étranglement dans le processus de démarrage.

Identifier les goulots d'étranglement

Pour identifier les goulots d'étranglement, utilisez le Profileur de processeur d'Android Studio. Pour en savoir plus, consultez Inspecter l'activité du processeur avec le Profileur de processeur.

Vous pouvez également obtenir des renseignements sur les goulots d'étranglement potentiels grâce au traçage intégré dans les méthodes onCreate() de vos applications et activités. Pour en savoir plus sur le traçage intégré, consultez la documentation sur les fonctions Trace et la présentation du traçage système.

Tenir compte des problèmes courants

Cette section traite de plusieurs problèmes qui affectent souvent les performances de démarrage des applications. Ces problèmes concernent principalement l'initialisation des objets d'application et d'activité, ainsi que le chargement des écrans.

Initialisation d'application à forte charge

Les performances de lancement peuvent être affectées lorsque votre code ignore l'objet Application et exécute un travail à forte charge ou une logique complexe lors de l'initialisation de cet objet. Votre application peut perdre du temps au démarrage si les sous-classes de l'application effectuent des initialisations qui ne sont pas nécessaires à ce stade. Certaines initialisations peuvent être complètement inutiles, comme l'initialisation des informations sur l'état de l'activité principale, lorsque l'application a démarré en réponse à un intent. Avec un intent, l'application n'utilise qu'un sous-ensemble des données d'état précédemment initialisées.

Lors de l'initialisation de l'application, d'autres défis incluent les événements de récupération de mémoire à impact élevé ou en quantité importante, ou les opérations d'E/S sur le disque exécutées simultanément à l'initialisation, ce qui bloque encore davantage le processus d'initialisation. La récupération de mémoire est particulièrement importante pour l'environnement d'exécution Dalvik. L'environnement d'exécution Art exécute la récupération de mémoire en même temps, ce qui réduit l'impact de cette opération.

Diagnostiquer le problème

Vous pouvez utiliser le traçage de méthode ou le traçage intégré pour essayer de diagnostiquer le problème.

Traçage de méthode

L'exécution du Profileur de processeur révèle que la méthode callApplicationOnCreate() appelle votre méthode com.example.customApplication.onCreate. Si l'outil montre que l'exécution de ces méthodes prend beaucoup de temps, vous devez examiner les tâches à effectuer de plus près.

Traçage intégré

Utilisez le traçage intégré pour identifier les causes probables de ces problèmes, y compris :

  • La fonction onCreate() initiale de votre application
  • Tout objet Singleton global initialisé par votre application
  • Toute opération d'E/S sur le disque, désérialisation ou boucle serrée qui peut se produire durant le goulot d'étranglement

Les solutions au problème

Que le problème soit dû à des initialisations inutiles ou à des opérations d'E/S sur le disque, l'initialisation différée est la solution. En d'autres termes, vous ne devez initialiser que les objets qui sont nécessaires immédiatement. Au lieu de créer des objets statiques globaux, passez à un modèle Singleton où l'application initialise les objets uniquement la première fois qu'elle en a besoin.

Envisagez également d'utiliser un framework d'injection de dépendances comme Hilt, qui crée des objets et des dépendances lors de leur première injection.

Si votre application utilise des fournisseurs de contenu pour initialiser les composants de l'application au démarrage, envisagez plutôt d'utiliser la bibliothèque de démarrage d'application.

Initialisation d'activité à forte charge

La création d'activités implique souvent d'importants efforts. Il est souvent possible d'optimiser cette tâche afin d'améliorer les performances. Les problèmes courants sont les suivants :

  • Gonflage des mises en page complexes ou de grande taille
  • Blocage de la visualisation à l'écran sur le disque ou l'E/S réseau
  • Chargement et décodage des bitmaps
  • Trame des objets VectorDrawable
  • Initialisation d'autres sous-systèmes de l'activité

Diagnostiquer le problème

Dans ce cas, le traçage de méthode et le traçage intégré peuvent également s'avérer utiles.

Traçage de méthode

Lorsque vous utilisez le Profileur de processeur, faites attention aux constructeurs de sous-classes Application et aux méthodes com.example.customApplication.onCreate() de votre application.

Si l'outil montre que l'exécution de ces méthodes prend beaucoup de temps, vous devez examiner les tâches à effectuer de plus près.

Traçage intégré

Utilisez le traçage intégré pour identifier les causes probables de ces problèmes, y compris :

  • La fonction onCreate() initiale de votre application
  • Tout objet Singleton global initialisé par votre application
  • Toute opération d'E/S sur le disque, désérialisation ou boucle serrée qui peut se produire durant le goulot d'étranglement

Les solutions au problème

Il existe de nombreux goulots d'étranglement potentiels, mais voici deux problèmes courants et leurs solutions :

  • Plus la hiérarchie des vues est grande, plus l'application met du temps à la gonfler. Pour résoudre ce problème, procédez comme suit :
    • Aplatissez la hiérarchie des vues en réduisant les mises en page redondantes ou imbriquées.
    • Ne gonflez pas les parties de l'interface utilisateur qui n'ont pas besoin d'être visibles lors du lancement. Utilisez plutôt un objet ViewStub comme espace réservé pour les sous-hiérarchies, que l'application peut gonfler à un moment plus approprié.
  • Initialiser l'ensemble de vos ressources sur le thread principal peut également ralentir le démarrage. Vous pouvez résoudre ce problème ainsi :
    • Déplacez l'initialisation de toutes les ressources afin que l'application puisse l'effectuer de manière différée sur un thread différent.
    • Autorisez l'application à charger et afficher vos vues, puis mettez à jour les propriétés visuelles qui dépendent de bitmaps et d'autres ressources.

Écrans de démarrage personnalisés

Le démarrage peut prendre plus de temps si vous avez déjà utilisé l'une des méthodes suivantes pour implémenter un écran de démarrage personnalisé sous Android 11 (niveau d'API 30) ou version antérieure :

  • Utilisation de l'attribut de thème windowDisablePreview pour désactiver l'écran vierge initial affiché par le système lors du lancement
  • Utilisation d'une activité dédiée

À partir d'Android 12, la migration vers l'API SplashScreen est requise. Cette API accélère le démarrage et vous permet d'ajuster votre écran de démarrage comme suit :

De plus, avec la bibliothèque de compatibilité, vous pouvez rétroporter l'API SplashScreen, ce qui permet d'activer la rétrocompatibilité et de créer une apparence cohérente pour les écrans de démarrage, quelle que soit la version d'Android.

Pour en savoir plus, consultez le guide de migration des écrans de démarrage.