L'utilisation d'images peut rapidement entraîner des problèmes de performances si vous n'y prenez pas garde. Même un petit graphique dans un format compressé tel que JPG ou PNG peut se transformer en grand bitmap lorsqu'il est décodé pour l'affichage. Si vous n'utilisez pas les graphiques de manière efficace, vous pouvez rencontrer des problèmes de mémoire qui peuvent nuire aux performances de votre application et d'autres applications sur l'appareil. Suivez ces bonnes pratiques pour optimiser les performances de votre application.
Utiliser des bibliothèques de chargement d'images
Vous pouvez améliorer l'efficacité de votre application en utilisant des bibliothèques de chargement d'images comme Coil (pour les projets Kotlin) ou Glide (pour les projets Java). Ces bibliothèques réduisent l'utilisation de la mémoire de votre application en mettant en cache les images, en réduisant la résolution des graphiques si nécessaire et en recyclant les objets graphiques, entre autres.
Sous-échantillonner les images
Assurez-vous d'utiliser la taille d'image appropriée à vos besoins. Évitez de charger une image volumineuse en haute résolution dans un petit conteneur (comme une miniature). Utilisez plutôt le sous-échantillonnage pour réduire la taille de l'image avant de la décoder en mémoire.
Sous-échantillonnage côté client
Les bibliothèques de chargement d'images telles que Coil et Glide gèrent automatiquement le sous-échantillonnage pour vous. Vous pouvez configurer leurs stratégies de sous-échantillonnage à l'aide de ImageLoader (pour Coil) ou DownsampleStrategy (pour Glide). Si vous gérez les bitmaps manuellement, vous pouvez utiliser inSampleSize pour décoder une version plus petite. Pour ce faire en toute sécurité, vous devez d'abord définir inJustDecodeBounds sur true pour lire les dimensions de l'image sans allouer de mémoire, calculer la taille de l'échantillon, définir inSampleSize sur cette valeur, définir inJustDecodeBounds sur false, puis décoder l'image.
Privilégier le redimensionnement côté serveur
Dans la mesure du possible, demandez les dimensions exactes des images dont vous avez besoin directement à votre serveur backend. Cela réduit l'utilisation du réseau et l'empreinte de votre cache disque, tout en offrant une utilisation de la mémoire plus légère en évitant la surcharge de mémoire liée au redimensionnement des images sur l'appareil.
Vous pouvez configurer les bibliothèques pour qu'elles ajoutent dynamiquement la taille de la vue cible à l'URL de l'image. Par exemple, Coil le permet à l'aide d'intercepteurs personnalisés, et Glide le prend en charge à l'aide de chargeurs de modèles personnalisés (tels que BaseGlideUrlLoader).
Éviter les tailles de mise en page non contraintes
Pour que les chargeurs d'images puissent sous-échantillonner efficacement (côté client ou côté serveur), ils doivent connaître la taille cible avant d'exécuter la requête.
Évitez d'utiliser wrapContentSize ou de laisser les dimensions sans contrainte sur les composables qui chargent des images à distance. Si ces bibliothèques ne peuvent pas déduire les limites cibles, elles reviennent au chargement de l'image d'origine en taille réelle.
Cela peut entraîner le chargement d'une image beaucoup plus grande que nécessaire, ce qui augmente l'utilisation de la mémoire et la latence.
Définissez plutôt des dimensions explicites sur votre composable d'image (par exemple, à l'aide de Modifier.size) ou définissez un format. Cela permet au moteur de mise en page de calculer à l'avance la cible de pixels exacte, que le chargeur d'images peut ensuite utiliser pour demander et décoder l'élément de taille appropriée.
Fournissez d'autres ressources pour différentes tailles d'écran
Si vous envoyez des images avec votre application, vous pouvez fournir des éléments de tailles différentes pour différentes résolutions d'appareils. Cela peut réduire la taille du téléchargement de votre application sur les appareils et améliorer les performances, car, sur un appareil de résolution inférieure, une image de résolution inférieure sera téléchargée. Pour en savoir plus sur la fourniture de différents bitmaps pour différentes tailles d'appareils, consultez la documentation sur les différents bitmaps.
Ne pas appliquer directement le remplissage
Vous devrez peut-être parfois ajouter une marge intérieure à une image. Par exemple, vous pouvez souhaiter que l'image soit entourée d'une bordure transparente pour le letterboxing.
Dans ce cas, n'ajoutez pas de marge intérieure directement à l'image, car cela modifierait ses dimensions. Laissez les dimensions de l'image telles quelles et ajustez sa position à l'écran à l'aide de InsetDrawable.
Vous pouvez également ajouter une marge intérieure au composable ou à la vue contenant l'image.
Choisir le bon format de pixel
Équilibrez la mémoire et la qualité en choisissant le bon format de pixel. Utilisez RGB_565 lorsque vous n'avez pas besoin de transparence. Ce format utilise la moitié de la mémoire du format ARGB_8888 par défaut.
Dans Glide, vous pouvez configurer cela à l'aide de DecodeFormat. Dans Coil, vous pouvez utiliser la propriété bitmapConfig.
Utilisez des vecteurs lorsque cela est possible
Pour les images composées de formes géométriques, un graphique vectoriel est beaucoup plus petit qu'un bitmap et s'adapte facilement à n'importe quelle densité d'affichage. Lorsque cela est approprié, utilisez des éléments tels que ShapeDrawable pour représenter des graphiques.
Libérez et réutilisez les bitmaps lorsque vous le pouvez
Les fichiers graphiques volumineux peuvent occuper beaucoup de mémoire. Pour réduire leur impact, vous devez libérer ou réutiliser les objets graphiques chaque fois que vous le pouvez.
Si vous utilisez une bibliothèque de chargement d'images, assurez-vous de libérer les bitmaps dans le pool géré de la bibliothèque lorsque vous n'en avez plus besoin. La bibliothèque peut réutiliser les objets si nécessaire et conserve un tampon de mémoire disponible pour les besoins futurs.
Si vous gérez les graphiques manuellement, vous devez libérer les bitmaps lorsque vous n'en avez plus besoin en appelant Bitmap.recycle et en supprimant immédiatement la référence Bitmap, au lieu de vous fier à la récupération de mémoire.
Autres conseils et astuces
Cette section présente d'autres façons d'améliorer les performances de votre application lors de la gestion des graphiques.
N'empaquetez pas d'images volumineuses avec votre fichier AAB/APK
L'une des principales causes de la taille importante des téléchargements d'applications est l'empaquetage des éléments graphiques dans le fichier AAB ou APK. Utilisez l'analyseur APK pour vous assurer que vous n'empaquetez pas des fichiers image plus volumineux que nécessaire. Réduisez la taille des images, ou placez-les sur un serveur et ne les téléchargez que lorsque cela est nécessaire.
Rechercher les bitmaps redondants
Si vous avez plusieurs copies de la même image, cela gaspille de la mémoire. Vous pouvez utiliser le profileur Android Studio pour identifier les graphiques redondants. Utilisez l'analyseur d'empreinte de la mémoire pour capturer une empreinte de la mémoire et filtrez les résultats en sélectionnant le paramètre Bitmaps en double.
Lorsque vous utilisez ImageBitmap, appelez prepareToDraw avant de dessiner
Lorsque vous utilisez ImageBitmap, pour lancer le processus d'importation de la texture dans le GPU, appelez ImageBitmap#prepareToDraw() avant de la dessiner. Cela permet au GPU de préparer la texture et d'améliorer les performances d'affichage d'un élément visuel à l'écran. Bien que la plupart des bibliothèques de chargement d'images effectuent déjà cette optimisation, si vous utilisez la classe ImageBitmap, gardez cela à l'esprit.
Préférez transmettre un Int DrawableRes ou une URL comme paramètres dans votre composable plutôt que Painter
La gestion des images étant complexe (par exemple, écrire une fonction égale pour Bitmaps est coûteux en ressources informatiques), l'API Painter n'est pas explicitement marquée comme stable avec l'annotation @Stable. Les classes instables peuvent entraîner des recompositions inutiles, car le compilateur ne peut pas facilement déterminer si les données ont changé.
Par conséquent, nous vous recommandons de transmettre une URL ou un ID de ressource drawable comme paramètres à votre composable, plutôt que de transmettre un Painter en tant que paramètre.
// Prefer this:
@Composable
fun MyImage(url: String) {
}
// Over this:
@Composable
fun MyImage(painter: Painter) {
}
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- ImageBitmap et ImageVector {:#bitmap-vs-vector}
- Enregistrer l'état de l'UI dans Compose
- Phases de Jetpack Compose