Wenn Sie nicht aufpassen, kann die Arbeit mit Bildern schnell zu Leistungsproblemen führen. Selbst eine kleine Grafik in einem komprimierten Format wie JPG oder PNG kann zu einer großen Bitmap werden, wenn sie für die Anzeige dekodiert wird. Wenn Sie Grafiken nicht effizient verwenden, kann es zu Arbeitsspeicherproblemen kommen, die die Leistung Ihrer App und anderer Apps auf dem Gerät beeinträchtigen. Mit diesen Best Practices können Sie die Leistung Ihrer App optimieren.
Bibliotheken zum Laden von Bildern verwenden
Sie können die Effizienz Ihrer App verbessern, indem Sie Bibliotheken zum Laden von Bildern wie Coil (für Kotlin-basierte Projekte) oder Glide (für Java-basierte Projekte) verwenden. Diese Bibliotheken reduzieren die Arbeitsspeichernutzung Ihrer App, indem sie beispielsweise Bilder im Cache speichern, Grafiken bei Bedarf downsamplen und Grafikobjekte wiederverwenden.
Bilder heruntersampeln
Achten Sie darauf, dass Sie die richtige Bildgröße für Ihre Anforderungen verwenden. Sie sollten kein großes Bild mit hoher Auflösung in einen kleinen Container (z. B. ein Thumbnail) laden. Verwenden Sie stattdessen Downsampling, um das Bild zu verkleinern, bevor es in den Arbeitsspeicher decodiert wird.
Clientseitiges Downsampling
Bibliotheken zum Laden von Bildern wie Coil und Glide übernehmen das Downsampling automatisch für Sie. Sie können die Downsampling-Strategien mit ImageLoader (für Coil) oder DownsampleStrategy (für Glide) konfigurieren. Wenn Sie Bitmaps manuell verwalten, können Sie mit inSampleSize eine kleinere Version decodieren. Um dies sicher zu tun, sollten Sie zuerst inJustDecodeBounds auf true setzen, um die Bildabmessungen zu lesen, ohne Speicher zuzuweisen, die Stichprobengröße berechnen, inSampleSize auf diesen Wert setzen, inJustDecodeBounds auf false setzen und dann das Bild decodieren.
Serverseitige Größenanpassung bevorzugen
Fordern Sie nach Möglichkeit die genauen Bildabmessungen, die Sie benötigen, direkt von Ihrem Backend-Server an. Dadurch wird die Netzwerknutzung und der Speicherplatzbedarf des Festplatten-Caches reduziert. Außerdem wird der Arbeitsspeicher weniger belastet, da das Anpassen der Größe von Bildern auf dem Gerät vermieden wird.
Sie können Bibliotheken so konfigurieren, dass die Zielansichtsgröße dynamisch an die Bild-URL angehängt wird. Coil ermöglicht dies beispielsweise mit benutzerdefinierten Interceptoren und Glide unterstützt es mit benutzerdefinierten Modell-Loadern (z. B. BaseGlideUrlLoader).
Layoutgrößen ohne Einschränkungen vermeiden
Damit Bild-Ladeprogramme effektiv downsamplen können (clientseitig oder serverseitig), müssen sie die Zielgröße vor der Ausführung der Anfrage kennen.
Verwenden Sie keine wrapContentSize-Werte und lassen Sie die Abmessungen nicht für Composables ohne Einschränkungen, die Remote-Bilder laden. Wenn die Zielbegrenzungen nicht abgeleitet werden können, wird das Originalbild in voller Größe geladen.
Dies kann dazu führen, dass ein wesentlich größeres Bild als nötig geladen wird, was die Arbeitsspeichernutzung und die Latenz erhöht.
Legen Sie stattdessen explizite Abmessungen für die zusammensetzbare Funktion des Bildes fest (z. B. mit Modifier.size) oder definieren Sie ein Seitenverhältnis. So kann die Layout-Engine das genaue Pixelziel im Voraus berechnen, das das Bild-Ladeprogramm dann verwenden kann, um das Asset in der richtigen Größe anzufordern und zu decodieren.
Alternative Ressourcen für verschiedene Bildschirmgrößen bereitstellen
Wenn Sie Bilder mit Ihrer App ausliefern, sollten Sie Assets in verschiedenen Größen für unterschiedliche Geräteauflösungen bereitstellen. So kann die Downloadgröße Ihrer App auf Geräten reduziert und die Leistung verbessert werden, da auf Geräten mit niedrigerer Auflösung Bilder mit niedrigerer Auflösung geladen werden. Weitere Informationen zum Bereitstellen alternativer Bitmaps für verschiedene Gerätegrößen finden Sie in der Dokumentation zu alternativen Bitmaps.
Padding nicht direkt anwenden
Manchmal müssen Sie einem Bild einen Innenabstand hinzufügen. Sie können beispielsweise einen transparenten Rahmen um das Bild legen, um Letterboxing zu erzielen.
In solchen Fällen sollten Sie das Padding nicht direkt dem Bild hinzufügen, da sich dadurch die Abmessungen des Bildes ändern. Lassen Sie die Abmessungen des Bildes unverändert und passen Sie die Position des Bildes auf dem Bildschirm mit InsetDrawable an.
Alternativ können Sie das Padding auch dem Composable oder der View hinzufügen, in dem sich das Bild befindet.
Das richtige Pixelformat auswählen
Wählen Sie das richtige Pixelformat, um ein ausgewogenes Verhältnis zwischen Speicherplatz und Qualität zu erzielen. Verwenden Sie RGB_565, wenn Sie keine Transparenz benötigen. Dieses Format benötigt nur halb so viel Speicher wie das Standardformat ARGB_8888.
In Glide können Sie dies mit DecodeFormat konfigurieren. In Coil können Sie die Property bitmapConfig verwenden.
Nach Möglichkeit Vektoren verwenden
Bei Bildern, die aus geometrischen Formen bestehen, ist eine Vektorgrafik viel kleiner als eine Bitmap und lässt sich für jede Displaydichte problemlos skalieren. Verwenden Sie nach Möglichkeit Elemente wie ShapeDrawable, um Grafiken darzustellen.
Bitmaps freigeben und wiederverwenden, wenn möglich
Große Grafikdateien können viel Speicherplatz belegen. Um die Auswirkungen zu verringern, sollten Sie die Grafikobjekte nach Möglichkeit freigeben oder wiederverwenden.
Wenn Sie eine Bibliothek zum Laden von Bildern verwenden, geben Sie Bitmaps für den verwalteten Pool der Bibliothek frei, wenn Sie sie nicht mehr benötigen. Die Bibliothek kann die Objekte bei Bedarf wiederverwenden und hält einen Speicherpuffer für zukünftige Anforderungen bereit.
Wenn Sie Grafiken manuell verwalten, sollten Sie Bitmaps freigeben, wenn Sie sie nicht mehr benötigen. Rufen Sie dazu Bitmap.recycle auf und verwerfen Sie sofort die Bitmap-Referenz, anstatt sich auf die automatische Speicherbereinigung zu verlassen.
Weitere Tipps und Tricks
In diesem Abschnitt werden einige weitere Möglichkeiten zur Verbesserung der Leistung Ihrer App bei der Verarbeitung von Grafiken aufgeführt.
Große Bilder nicht in Ihr AAB/Ihre APK-Datei einbinden
Eine der häufigsten Ursachen für eine große Downloadgröße von Apps sind Grafiken, die im AAB oder in der APK-Datei enthalten sind. Mit dem APK-Analysetool können Sie prüfen, ob Sie unnötig große Bilddateien in Ihr Paket aufnehmen. Reduzieren Sie die Größe oder erwägen Sie, die Bilder auf einem Server zu platzieren und nur bei Bedarf herunterzuladen.
Redundante Bitmaps finden
Wenn Sie mehrere Kopien desselben Bildes haben, verschwenden Sie Speicherplatz. Mit dem Android Studio-Profiler lassen sich redundante Grafiken ermitteln. Verwenden Sie den Heap-Dump-Analyzer, um einen Heap-Dump zu erfassen, und filtern Sie die Ergebnisse, indem Sie die Einstellung Doppelte Bitmaps auswählen.
Wenn Sie ImageBitmap verwenden, rufen Sie prepareToDraw vor dem Zeichnen auf.
Wenn Sie ImageBitmap verwenden, rufen Sie ImageBitmap#prepareToDraw() auf, bevor Sie die Textur zeichnen, um den Upload auf die GPU zu starten. Dadurch kann die GPU die Textur vorbereiten und die Leistung bei der Darstellung eines Bildes auf dem Bildschirm verbessern. Die meisten Bibliotheken zum Laden von Bildern führen diese Optimierung bereits durch. Wenn Sie jedoch selbst mit der Klasse ImageBitmap arbeiten, sollten Sie dies berücksichtigen.
Übergeben Sie lieber Int DrawableRes oder eine URL als Parameter an Ihre zusammensetzbare Funktion anstelle von Painter.
Aufgrund der Komplexität des Umgangs mit Bildern (z. B. wäre das Schreiben einer Gleichheitsfunktion für Bitmaps rechenintensiv) ist die Painter API explizit nicht mit der Annotation @Stable als stabil gekennzeichnet. Instabile Klassen können zu unnötigen Recompositions führen, da der Compiler nicht einfach ableiten kann, ob sich die Daten geändert haben.
Wir empfehlen daher, eine URL oder eine ID für eine zeichenfähige Ressource als Parameter an Ihre komponierbare Funktion zu übergeben, anstatt ein Painter als Parameter zu übergeben.
// Prefer this:
@Composable
fun MyImage(url: String) {
}
// Over this:
@Composable
fun MyImage(painter: Painter) {
}
Persönliche Empfehlungen
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist
- Vergleich von ImageBitmap mit ImageVector {:#bitmap-vs-vector}
- UI-Status in Compose speichern
- Jetpack Compose-Phasen