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é comme JPG ou PNG peut se transformer en un bitmap volumineux lorsqu'il est décodé pour l'affichage. Si vous n'utilisez pas les graphismes de manière efficace, vous risquez de rencontrer des problèmes de mémoire qui peuvent nuire aux performances de votre application et des autres applications de 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 telles que Coil (pour les projets Kotlin-first) ou Glide (pour les projets Java). Ces bibliothèques réduisent l'utilisation de la mémoire de votre application en effectuant des opérations telles que la mise en cache des images, le sous-échantillonnage des graphiques si nécessaire et le recyclage des objets graphiques.
Sous-échantillonner les images
Veillez à utiliser la taille d'image adapté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 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. Vous pouvez configurer leurs stratégies de sous-échantillonnage à l'aide de ImageLoader (pour Coil) ou de 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 de l'image dont vous avez besoin directement à partir de votre serveur backend. Cela réduit l'utilisation du réseau et l'empreinte du cache du disque, tout en réduisant l'utilisation de la mémoire en évitant la surcharge de mémoire liée au redimensionnement des images sur l'appareil.
Vous pouvez configurer des bibliothèques pour ajouter de manière dynamique la taille de la vue cible à l'URL de l'image. Par exemple, Coil permet d'utiliser des intercepteurs personnalisés, et Glide est compatible avec les chargeurs de modèles personnalisés (comme BaseGlideUrlLoader).
Éviter les tailles de mise en page sans contrainte
Pour que les chargeurs d'images puissent sous-échantillonner (côté client ou côté serveur) efficacement, 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 distantes. Si ces bibliothèques ne peuvent pas déduire les limites cibles, elles chargent l'image d'origine en taille réelle.
Cela peut entraîner le chargement d'une image beaucoup plus volumineuse 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 exacte en pixels, que le chargeur d'images peut ensuite utiliser pour demander et décoder l'élément de la bonne taille.
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.
N'appliquez pas de marge intérieure directement
Vous devrez parfois ajouter une marge intérieure à une image. Par exemple, vous pouvez entourer l'image d'une bordure transparente pour le letterboxing.
Dans ce cas, n'ajoutez pas la marge intérieure directement à l'image, car cela modifierait ses dimensions. Laissez plutôt les dimensions de l'image telles quelles et ajustez son emplacement à l'écran à l'aide de InsetDrawable.
Vous pouvez également ajouter une marge intérieure dans le composable ou la vue contenant l'image.
Choisir le bon format de pixel
Trouvez le bon équilibre entre mémoire et 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.
Utiliser des vecteurs dans la mesure du 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. Le cas échéant, 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, veillez à 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 graphismes manuellement, vous devez libérer les bitmaps lorsque vous avez terminé de les utiliser en appelant Bitmap.recycle et en supprimant immédiatement la référence Bitmap, au lieu de vous fier au garbage collection.
Autres conseils et astuces
Cette section présente d'autres moyens d'améliorer les performances de votre application lors de la gestion des graphismes.
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, puis filtrez les résultats en choisissant le paramètre duplicate bitmaps (bitmaps en double).
Si 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