Optimiser l'utilisation de la mémoire

L'optimisation de la mémoire est essentielle pour assurer des performances fluides, éviter les plantages d'applications et maintenir la stabilité du système et l'état de la plate-forme. Bien que l'utilisation de la mémoire doive être surveillée et optimisée dans chaque application, les applications de contenu pour les téléviseurs présentent des défis spécifiques qui diffèrent des applications Android classiques pour les appareils portables.

Une consommation élevée de mémoire peut entraîner des problèmes de comportement de l'application et du système, y compris les suivants:

  • L'application elle-même peut devenir lente ou latence, ou dans le pire des cas, être arrêtée.
  • Les services système visibles par l'utilisateur (commande du volume, tableau de bord des paramètres d'image, Assistant Voice, etc.) deviennent très lents ou peuvent ne pas fonctionner du tout.
  • Les composants système peuvent être arrêtés, puis redémarrés, ce qui déclenche des pics de contention de ressources extrêmes et a un impact direct sur l'application de premier plan.
  • La transition vers le lanceur d'applications peut être considérablement retardée et laisser l'application de premier plan apparaître comme non réactive jusqu'à la fin de la transition.
  • Le système peut entrer dans une situation de récupération directe, en suspendant temporairement l'exécution d'un thread en attendant l'allocation de mémoire. Cela peut arriver à n'importe quel thread, comme le thread principal ou les threads liés au codec, ce qui peut entraîner des pertes de frames audio et vidéo, ainsi que des problèmes d'UI.

Considérations concernant la mémoire sur les appareils TV

Les appareils TV ont généralement beaucoup moins de mémoire que les téléphones ou les tablettes. Par exemple, une configuration que vous pouvez voir sur un téléviseur est 1 Go de RAM et une résolution vidéo de 1080p. En même temps, la plupart des applications TV présentent des fonctionnalités similaires, ce qui implique une implémentation et des défis similaires. Ces deux situations présentent des problèmes que vous ne rencontrez pas avec d'autres types d'appareils et d'applications:

  • Les applications TV multimédias sont généralement composées à la fois de vues d'images en grille et de images d'arrière-plan en plein écran, qui nécessitent de charger de nombreuses images dans la mémoire en peu de temps.
  • Les applications TV lisent des flux multimédias qui nécessitent d'allouer une certaine quantité de mémoire pour lire des vidéos et de l'audio, et ont besoin de tampons multimédias considérables pour assurer une lecture fluide.
  • Les fonctionnalités multimédias supplémentaires (recherche, changement d'épisode, changement de piste audio, etc.) peuvent exercer une pression supplémentaire sur la mémoire si elles ne sont pas implémentées correctement.

Comprendre les appareils TV

Ce guide porte principalement sur l'utilisation de la mémoire des applications et les cibles de mémoire pour les appareils à faible capacité de RAM.

Sur les appareils TV, tenez compte des caractéristiques suivantes:

  • Mémoire de l'appareil: quantité de mémoire vive (RAM) installée sur l'appareil.
  • Résolution de l'interface utilisateur de l'appareil: résolution utilisée par l'appareil pour afficher l'interface utilisateur de l'OS et des applications. Elle est généralement inférieure à la résolution vidéo de l'appareil.
  • Résolution vidéo: résolution maximale à laquelle l'appareil peut lire des vidéos.

Cela permet de classer les différents types d'appareils et la façon dont ils doivent utiliser la mémoire.

Résumé des appareils TV

Mémoire de l'appareil Résolution vidéo de l'appareil Résolution de l'interface utilisateur de l'appareil isLowRAMDevice()
1 Go 1080p 720p Oui
1,5 Go 2160p 1080p Oui
≥ 1,5 Go 1080p 720p ou 1080p Non*
≥ 2 Go 2160p 1080p Non*

Appareils TV à faible RAM

Ces appareils se trouvent dans une situation de mémoire limitée et indiquent que ActivityManager.isLowRAMDevice() est défini sur "True". Les applications exécutées sur des appareils TV à faible RAM doivent implémenter des mesures de contrôle de la mémoire supplémentaires.

Les appareils présentant les caractéristiques suivantes sont considérés comme appartenant à cette catégorie:

  • Appareils 1 Go: 1 Go de RAM, résolution de l'interface utilisateur 720p/HD (1 280 x 720), résolution vidéo 1080p/FullHD (1 920 x 1 080)
  • Appareils 1,5 Go: 1,5 Go de RAM, résolution de l'interface utilisateur 1080p/FullHD (1 920 x 1 080), résolution vidéo 2 160p/UltraHD/4K (3 840 x 2 160)
  • Autres situations dans lesquelles l'OEM a défini l'indicateur ActivityManager.isLowRAMDevice() en raison de contraintes de mémoire supplémentaires.

Appareils TV standards

Ces appareils ne sont pas soumis à une pression de mémoire aussi importante. Nous considérons que ces appareils présentent les caractéristiques suivantes:

  • 1,5 Go de RAM minimum, interface utilisateur en 720p ou 1080p et résolution vidéo 1080p
  • ≥ 2 Go de RAM, interface utilisateur 1080p et résolution vidéo 1080p ou 2160p

Cela ne signifie pas que les applications ne doivent pas se soucier de l'utilisation de la mémoire sur ces appareils, car certains cas de mauvaise utilisation de la mémoire peuvent toujours épuiser la mémoire disponible et entraîner de mauvaises performances.

Cibles de mémoire sur les appareils TV à faible RAM

Lorsque vous mesurez la mémoire sur ces appareils, nous vous recommandons vivement de surveiller chaque section de la mémoire à l'aide du profileur de mémoire Android Studio. Les applications TV doivent profiler leur utilisation de la mémoire et s'efforcer de placer leurs catégories en dessous des seuils que nous définissons dans cette section.

Profileur de mémoire

La section Comment la mémoire est comptabilisée contient une explication détaillée des chiffres de mémoire indiqués. Pour définir les seuils des applications TV, nous allons nous concentrer sur trois catégories de mémoire:

  • Anonymous + Swap: composé de mémoire d'allocation Java + Native + Stack dans Android Studio.
  • Graphiques: signalés directement dans l'outil de profilage. Généralement composé de textures graphiques.
  • Fichier: signalé dans les catégories "Code" et "Autre" d'Android Studio.

Avec ces définitions, le tableau suivant indique la valeur maximale que chaque type de groupe de mémoire doit utiliser:

Type de mémoire Objectif Cibles d'utilisation (1 Go)
Anonyme + échange (Java + natif + pile) Utilisé pour les allocations, les tampons multimédias, les variables et d'autres tâches gourmandes en mémoire. 160 Mo ou moins
Graphiques Utilisé par le GPU pour les textures et les tampons associés à l'affichage 30 à 40 Mo
Fichier Utilisé pour les pages de code et les fichiers en mémoire. 60 à 80 Mo

La mémoire totale maximale (Anon+Swap+Graphics+File) ne doit pas dépasser les valeurs suivantes:

  • 280 Mo d'utilisation totale de la mémoire (Anon+Swap+Graphics+File) pour les appareils à faible mémoire RAM de 1 Go.

Nous vous recommandons vivement de ne pas dépasser les valeurs suivantes:

  • 200 Mo d'utilisation de la mémoire sur (Anon+Swap + Graphics).

Mémoire de fichier

Voici quelques conseils généraux concernant la mémoire basée sur des fichiers:

  • En général, la mémoire de fichier est bien gérée par la gestion de la mémoire de l'OS.
  • Pour le moment, nous n'avons pas constaté qu'il s'agissait d'une cause majeure de pression sur la mémoire.

Toutefois, en règle générale, lorsque vous travaillez avec la mémoire de fichier:

  • N'incluez pas de bibliothèques inutilisées dans votre build, et utilisez de petits sous-ensembles de bibliothèques plutôt que les bibliothèques complètes lorsque cela est possible.
  • Ne laissez pas les fichiers volumineux ouverts dans la mémoire et libérez-les dès que vous n'en avez plus besoin.
  • Pour minimiser la taille de votre code compilé pour les classes Java et Kotlin, consultez le guide Réduire, obscurcir et optimiser votre application.

Recommandations TV spécifiques

Cette section fournit des recommandations spécifiques pour optimiser l'utilisation de la mémoire sur les appareils TV.

Mémoire graphique

Utilisez des formats et des résolutions d'image appropriés.

  • Ne chargez pas d'images dont la résolution est supérieure à celle de l'interface utilisateur de l'appareil. Par exemple, les images 1080p doivent être réduites à 720p sur un appareil avec une interface utilisateur 720p.
  • Utilisez des bitmaps basés sur le matériel dans la mesure du possible.
    • Dans les bibliothèques telles que Glide, activez la fonctionnalité Downsampler.ALLOW_HARDWARE_CONFIG, qui est désactivée par défaut. Cette option permet d'éviter de dupliquer les bitmaps qui se trouveraient autrement à la fois dans la mémoire graphique et dans la mémoire anonyme.
  • Éviter les rendus intermédiaires et les re-rendus
    • Vous pouvez les identifier avec Android GPU Inspector:
    • Dans la section "Textures", recherchez les images qui sont des étapes vers le rendu final plutôt que les éléments qui les constituent. Il s'agit généralement d'un rendu intermédiaire.
    • Pour les applications du SDK Android, vous pouvez souvent les supprimer à l'aide de l'indicateur de mise en page forceHasOverlappedRendering:false pour désactiver les rendus intermédiaires pour cette mise en page.
    • Consultez la section Éviter les rendus qui se chevauchent pour en savoir plus sur ce problème.
  • Évitez de charger des images d'espace réservé lorsque cela est possible. Utilisez @android:color/ ou @color pour les textures d'espace réservé.
  • Évitez de composer plusieurs images sur l'appareil lorsque la composition peut être effectuée hors connexion. Privilégier le chargement d'images autonomes plutôt que de composer des images à partir d'images téléchargées
  • Suivez le guide Gérer les bitmaps pour mieux les gérer.

Mémoire Anon+Swap

Anon+Swap est composé d'allocations natives, Java et de pile dans le profileur de mémoire Android Studio. Utilisez ActivityManager.isLowMemoryDevice() pour vérifier si l'appareil est soumis à des contraintes de mémoire et adaptez-vous à cette situation en suivant ces consignes.

  • Médias:
    • Spécifiez une taille variable pour les tampons multimédias en fonction de la RAM de l'appareil et de la résolution de lecture vidéo. Cela devrait correspondre à une minute de lecture vidéo :
      1. 40 à 60 Mo pour 1 Go / 1080p
      2. 60 à 80 Mo pour 1,5 Go / 1080p
      3. 80 à 100 Mo pour 1,5 Go / 2160p
      4. 100 à 120 Mo pour 2 Go / 2160p
    • Libération des allocations de mémoire multimédia lors du changement d'un épisode pour éviter une augmentation de la quantité totale de mémoire anonyme.
    • Libérez et arrêtez immédiatement les ressources multimédias lorsque votre application est arrêtée: utilisez les rappels de cycle de vie de l'activité pour gérer les ressources audio et vidéo. Si vous n'êtes pas une application audio, arrêtez la lecture lorsque onStop() se produit dans vos activités, enregistrez tout le travail que vous effectuez et libérez vos ressources. Pour planifier des tâches dont vous pourriez avoir besoin plus tard. Consultez la section Jobs et alarmes.
    • Faites attention à la mémoire du tampon lors de la recherche de vidéo: les développeurs allouent souvent 15 à 60 secondes supplémentaires de contenu futur lorsqu'ils cherchent à préparer la vidéo pour l'utilisateur, mais cela crée une surcharge de mémoire supplémentaire. En règle générale, ne prenez pas plus de cinq secondes de tampon avant que l'utilisateur n'ait sélectionné la nouvelle position de la vidéo. Si vous devez absolument pré-bufferiser du temps supplémentaire lors de la recherche, assurez-vous de :
      • Allouez le tampon de recherche à l'avance et réutilisez-le.
      • La taille de la mémoire tampon ne doit pas dépasser 15 à 25 Mo (selon la mémoire de l'appareil).
  • Allocations:
    • Suivez les conseils sur la mémoire graphique pour vous assurer de ne pas dupliquer d'images dans la mémoire anonyme.
      • Les images sont souvent les plus gros consommateurs de mémoire. Leur duplication peut donc mettre beaucoup de pression sur l'appareil. Cela est particulièrement vrai lors d'une navigation intensive dans les grilles d'images.
    • Libérez les allocations en supprimant leurs références lorsque vous déplacez des écrans : assurez-vous qu'il ne reste aucune référence aux bitmaps et aux objets.
  • Bibliothèques:
    • Profil des allocations de mémoire à partir des bibliothèques lorsque vous en ajoutez, car elles peuvent également charger des bibliothèques supplémentaires, ce qui peut également effectuer des allocations et créer des liaisons.
  • Mise en réseau:
    • N'effectuez pas d'appels réseau bloquants au démarrage de l'application. Ils ralentissent le temps de démarrage de l'application et créent des frais généraux de mémoire supplémentaires au démarrage, où la mémoire est particulièrement limitée par la charge de l'application. Affichez d'abord un écran de chargement ou d'accueil, puis effectuez des requêtes réseau une fois l'UI en place.

Liaisons

Les liaisons introduisent des frais généraux de mémoire supplémentaires, car elles introduisent d'autres applications dans la mémoire ou augmentent la consommation de mémoire de l'application liée (si elle est déjà en mémoire) pour faciliter l'appel d'API. Par conséquent, cela réduit la mémoire disponible pour l'application de premier plan. Lorsque vous liez un service, tenez compte du moment et de la durée d'utilisation de la liaison. Veillez à libérer la liaison dès qu'elle n'est plus nécessaire.

Liaisons standards et bonnes pratiques:

  • API Play Integrity: permet de vérifier l'intégrité de l'appareil.
    • Vérifier l'intégrité de l'appareil après l'écran de chargement et avant la lecture multimédia
    • Libérez les références à PlayIntegrity StandardIntegrityManager avant de lire le contenu.
  • Bibliothèque Play Billing: permet de gérer les abonnements et les achats via Google Play.
  • GMS FontsProvider
    • Privilégiez les polices autonomes sur les appareils à faible RAM plutôt que d'utiliser le fournisseur de polices, car le téléchargement des polices est coûteux et FontsProvider lie des services pour le faire.
  • Bibliothèque Assistant Google: parfois utilisée pour la recherche et la recherche dans l'application. Si possible, remplacez cette bibliothèque.
    • Pour les applications leanback: utilisez la synthèse vocale Gboard ou la bibliothèque androidx.leanback.
    • Pour les applications Compose :
      • Utilisez la synthèse vocale Gboard pour implémenter la recherche vocale.
    • Implémentez À regarder ensuite pour rendre les contenus multimédias de votre application visibles.

Services de premier plan

Les services de premier plan sont un type de service spécial associé à une notification. Cette notification s'affiche dans le bac de notifications sur les téléphones et les tablettes, mais les téléviseurs ne disposent pas d'un bac de notifications au même sens que ces appareils. Même si les services de premier plan sont utiles, car ils peuvent continuer à s'exécuter lorsque l'application est en arrière-plan, les applications TV doivent respecter les consignes suivantes:

Sur Android TV et Google TV, les services de premier plan ne sont autorisés à continuer à s'exécuter que lorsque l'utilisateur quitte l'application:

  • Pour les applications audio:les services de premier plan ne sont autorisés à continuer à s'exécuter que lorsque l'utilisateur quitte l'application pour continuer à lire le titre audio. Le service doit être arrêté immédiatement après la fin de la lecture audio.
  • Pour toute autre application:tous les services de premier plan doivent être arrêtés une fois que l'utilisateur quitte votre application, car aucune notification n'est envoyée pour l'informer que l'application est toujours en cours d'exécution et consomme des ressources.
  • Pour les tâches en arrière-plan telles que la mise à jour des recommandations ou la section À regarder ensuite, utilisez WorkManager.

Tâches et alarmes

WorkManager est l'API Android de pointe pour planifier des tâches récurrentes en arrière-plan. WorkManager utilisera le nouveau JobScheduler lorsqu'il sera disponible (SDK 23 et versions ultérieures) et l'ancien AlarmManager lorsqu'il ne l'est pas. Pour connaître les bonnes pratiques à suivre lorsque vous effectuez des tâches planifiées sur une télévision, suivez les recommandations ci-dessous:

  • Évitez d'utiliser les API AlarmManager, sur le SDK 23 ou version ultérieure, en particulier AlarmManager.set(), AlarmManager.setExact() et les méthodes similaires, car elles n'autorisent pas le système à décider du moment opportun pour exécuter les tâches (par exemple, lorsque l'appareil est au ralenti).
  • Sur les appareils disposant d'une faible quantité de RAM, évitez d'exécuter des tâches, sauf si cela est strictement nécessaire. Si nécessaire, utilisez WorkManager WorkRequest uniquement pour mettre à jour les recommandations après la lecture et essayez de le faire lorsque l'application est encore ouverte.
  • Définissez WorkManager Constraints pour permettre au système d'exécuter vos tâches au moment opportun:

Kotlin

Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()

Java

Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
  • Si vous devez exécuter des tâches régulièrement (par exemple, pour mettre à jour Regarder la suite en fonction de l'activité de visionnage de contenu d'un utilisateur dans votre application sur un autre appareil), limitez l'utilisation de la mémoire en maintenant la consommation de mémoire de la tâche en dessous de 30 Mo.

Autres consignes générales

Les consignes suivantes fournissent des informations générales sur le développement d'applications Android:

  • Réduisez les allocations d'objets, optimisez la réutilisation des objets et désallouez rapidement les objets inutilisés.
    • Ne conservez pas de références aux objets, en particulier aux bitmaps.
    • Évitez d'utiliser System.gc() et les appels de mémoire de libération directe, car ils interfèrent avec le processus de gestion de la mémoire du système. Par exemple, sur les appareils utilisant la zRAM, un appel forcé à gc() peut temporairement augmenter l'utilisation de la mémoire en raison de la compression et de la décompression de la mémoire.
    • Utilisez LazyList, comme illustré dans un navigateur de catalogue dans Compose ou RecyclerView dans le kit d'UI Leanback désormais obsolète pour réutiliser les vues et ne pas recréer les éléments de liste.
    • Mettez en cache localement les éléments lus à partir de fournisseurs de contenu externes qui ne sont pas susceptibles de changer et définissez des intervalles de mise à jour qui empêchent d'allouer de la mémoire externe supplémentaire.
  • Recherchez d'éventuelles fuites de mémoire.
    • Veillez à surveiller les cas de fuites de mémoire typiques, tels que les références dans des threads anonymes, la réallocation de tampons vidéo qui ne sont jamais libérés et d'autres situations similaires.
    • Utilisez un dump de tas de mémoire pour déboguer les fuites de mémoire.
  • Générez des profils de référence pour réduire la quantité de compilation juste-à-temps nécessaire lors de l'exécution de votre application lors d'un démarrage à froid.

Récapitulatif des outils