Was es mit dem Project Svelte in Android KitKat auf sich hat

Android KitKat: Speicherschonender Schokoriegel

Lars Röwekamp, Arne Limburg
© Shutterstock / Palto

Seit einigen Monaten ist Android KitKat (4.4) released. Nachdem die Vorgängerversionen mit dem Project Butter den Fokus auf höhere Performance gelegt haben, liegt der Schwerpunkt von Android 4.4 auf dem geringeren Speicherverbrauch. Erreicht wurde dies durch das Project Svelte. Damit einher geht auch eine bessere Unterstützung von Low-Budget-Geräten, die von Haus aus über wenig Speicher verfügen.

Der wachsende Funktionsumfang von Android führte in den vergangenen Versionen immer mehr dazu, dass auch der Speicherverbrauch deutlich anstieg. Dementgegen steht eine immer größere Marktverbreitung und damit einhergehend eine wachsende Anzahl an Low-Budget-Geräten. Auf diesen, in der Regel mit wenig Arbeitsspeicher ausgestatteten Geräten waren neuere Android-Versionen aufgrund des hohen Speicherbedarfs häufig so langsam, dass sie praktisch nicht zu benutzen waren.

Zwar schraubten die Android-Entwickler bereits in den Android-Versionen 4.1, 4.2 und 4.3 (alle unter dem Namen Jelly Bean released) erheblich an der Performance, aber erst mit Version 4.4 wurde das Problem des Arbeitsspeicherbedarfs angegangen.

Project Svelte

Geschehen ist das mit dem Project Svelte, in dessen Rahmen nicht nur die Core-Funktionen von Android allesamt auf geringen Speicherbedarf hin optimiert wurden. Zusätzlich wurde (ehemalige) Core-Funktionalität in externe Google-Apps ausgelagert, was zur Folge hat, dass sie sich jetzt nicht mehr zwingend im Speicher befinden müssen, wenn sie nicht benötigt werden. Außerdem wurde den App-Entwicklern im Bereich Speicherbedarf unter die Arme gegriffen. Neben einem verbesserten Verfahren für das Recycling von Bildarbeitsspeicher (die Verwendung des Parameters BitmapFactory.Options.inBitmap wurde optimiert, dazu später mehr) gibt es nun eine Möglichkeit, auf Geräte mit wenig Arbeitsspeicher programmatisch zu reagieren. Dies kann über die Funktion ActivityManager.isLowRamDevice() geschehen, mit der, wie der Name schon sagt, überprüft werden kann, ob man sich auf einem Gerät mit wenig Arbeitsspeicher befindet. Was aber soll ein Entwickler tun, wenn er genau das feststellt?

Fallstricke beim Speichermanagement

Generell muss sich ein Java-Entwickler nicht um die Freigabe von nicht mehr benötigtem Arbeitsspeicher kümmern. Dafür sorgt automatisch der Garbage Collector. Und das gilt prinzipiell auch für Android-Entwickler.

Dennoch müssen insbesondere in Android einige Dinge beachtet werden, damit der Garbage Collector korrekt arbeiten kann. Dass er nur aufräumt, wenn keine Referenz auf eine Datenstruktur mehr existiert, sollte erfahrenen Entwicklern hinlänglich bekannt sein. Die direkte Konsequenz daraus ist natürlich, dass große Datenstrukturen wie z. B. Bilddaten nur als Instanzvariable (z. B. einer Activity) gehalten werden sollten, wenn sie tatsächlich noch benötigt werden. Sollten sie für die Lebenszeit der Activity notwendig sein, ist ein Halten als Instanzvariable normalerweise kein Problem, weil die Activity nach ihrer Beendigung mitsamt ihrer Instanzvariablen für die Garbage Collection freigegeben ist. Aber auch hier gibt es einige Stolpersteine.

So sollte z. B. keine nicht statische innere Klasse einer Activity in einem separaten Thread verwendet werden, ohne im onStop() der Activity dafür zu sorgen, dass der Thread beendet wird. Das liegt daran, dass Instanzen nicht-statischer innerer Klassen immer implizit eine Referenz auf die zugehörige Instanz der äußeren Klasse (in unserem Fall der Activity) halten. Die äußere Klasse kann dann so lange nicht vom Garbage Collector aufgeräumt werden, wie der Thread läuft. Wenn der Thread z. B. beim Start der Activity mitgestartet wird und der Benutzer das Handy vom Hoch- ins Querformat dreht (was zum Zerstören der Activity und dem Erzeugen einer neuen führt), bleibt die zerstörte Activity solange im Speicher, wie der erzeugte Thread läuft.

Speichermanagement von Bitmaps

Wenn die Dalvik VM feststellt, dass der Heap der App knapp wird, lässt sie automatisch den Garbage Collector laufen, um nicht mehr benötigten Speicher freigeben zu lassen.

Einen Stolperstein der besonderen Art halten Android-Versionen vor 3.0 (also 2.3.3 und kleiner) in diesem Bereich und im Zusammenhang mit dem Speichern von Bitmaps bereit. Die Bilddaten von Bitmaps wurden nicht im normalen Heap der Dalvik VM gehalten, sondern in einem separaten Speicherbereich. Der Heap enthielt nur eine (kleine) Referenz auf diesen externen Speicher. Wenn der Garbage Collector diese Referenz aufgeräumt hat, wurde automatisch auch der externe Speicher freigegeben. Zusätzlich konnte man das Freigeben des Speichers mit Bitmap.recycle() programmatisch forcieren.

Vorteil dieses Vorgehens war, dass der verfügbare Heap der App deutlich höher war als bei derselben App in höheren Android-Versionen. Der große Nachteil allerdings kam zum Tragen, wenn eine App viele Bitmaps parallel verwaltete. Dann nämlich konnte es passieren, dass die App stillschweigend beendet wurde, weil der externe Speicher überfüllt war. Für diesen Fall hatte auch der Garbage Collector nicht die Chance, vorher aufzuräumen, weil der Speicher ja gar nicht im Heap (für den der Garbage Collector zuständig ist) verbraucht wurde. Geholfen hat da nur der frühzeitige Aufruf von recycle(). Seit Android 3 existiert dieses Problem nicht mehr, weil Bitmap-Daten komplett im Heap liegen.

Optimierung von Bitmap-Speicher über Options

Generell führt das Arbeiten mit Bitmaps wegen des großen Speicherbedarfs zu Performanceeinbußen wegen der häufig laufenden Garbage Collection und dem häufigen Allokieren und Freigeben von großen Speicherbereichen. Da ist der Gedanke naheliegend, den Speicher eines Bitmaps gar nicht erst freizugeben, sondern zum Laden des nächsten Bitmaps direkt wiederzuverwenden. Möglich macht dies die Option BitmapFactory.Options.inBitmap, über die den verschiedenen BitmapFactory.decode…-Methoden ein wiederzuverwendendes Bitmap übergeben werden kann. Sofern der Speicher des übergebenen Bitmaps zur Wiederverwendung geeignet ist, wird er verwendet, um das neue Bitmap zu erzeugen. Dies spart das Allokieren und Freigeben von Speicher. Damit das übergebene Bitmap verwendet werden kann, muss es allerdings einige Anforderungen erfüllen. So war es vor Android 4.4 notwendig, dass es dieselbe Größe hatte, wie das zu erzeugende Bitmap. Diese Einschränkung wurde in 4.4 durch eine verbesserte Implementierung zwar gelockert, allerdings darf die Anzahl der Bytes des neuen Bitmaps die des alten nach wie vor nicht überschreiten.

Über die BitmapFactory.Options sind weitere Optimierungen möglich. So kann man über die Option inPurgeable in Verbindung mit inInputShareable dafür sorgen, dass die Bitmap-Daten bei knapp werdendem Speicher verworfen werden und bei Bedarf aus der primären Datenquelle (z. B. einem speicherschonenderem JPEG) wieder dekodiert werden.

Über die Option inSampleSize lässt sich das zu ladende Bitmap direkt skalieren (z. B. auf eine Größe, die an die Größe des Displays angepasst ist). Die tatsächliche Größe lässt sich zuvor über inJustDecodeBounds ermitteln, ohne das gesamte Bitmap in den Speicher zu laden.

Fazit

Android 4.4 (KitKat) wartet mit deutlich geringerem Speicherverbrauch auf. Das macht sich nicht nur auf Low-Budget-Geräten bemerkbar, sondern kann bei jedem Gerät zu einer höheren Gesamtperformance führen, insbesondere bei der Verwendung von speicherfressenden Apps.

Über die Funktion ActivityManager.isLowRamDevice() hat man in KitKat zudem die Möglichkeit innerhalb der eigenen App speziell auf Low-Budget-Geräte zu reagieren und den Speicherverbrauch der eigenen App darauf abzustimmen.

Unabhängig davon gibt es auch in älteren Android-Versionen einige Möglichkeiten, durch intelligentes Speichermanagement speicherschonend vorzugehen. Hierzu wurden im Rahmen dieser Kolumne Fallstricke aufgezeigt und einige Tricks vor allem im Umgang mit Bitmaps vorgestellt, um nicht so schnell in Speicherprobleme zu geraten.

Aufmacherbild: Sick robot von Shutterstock / Urheberrecht: Palto

Geschrieben von
Lars Röwekamp
Lars Röwekamp
Lars Röwekamp ist Gründer des IT-Beratungs- und Entwicklungsunternehmens open knowledge GmbH, beschäftigt sich im Rahmen seiner Tätigkeit als „CIO New Technologies“ mit der eingehenden Analyse und Bewertung neuer Software- und Technologietrends. Ein besonderer Schwerpunkt seiner Arbeit liegt derzeit in den Bereichen Enterprise und Mobile Computing, wobei neben Design- und Architekturfragen insbesondere die Real-Life-Aspekte im Fokus seiner Betrachtung stehen. Lars Röwekamp, Autor mehrerer Fachartikel und -bücher, beschäftigt sich seit der Geburtsstunde von Java mit dieser Programmiersprache, wobei er einen Großteil seiner praktischen Erfahrungen im Rahmen großer internationaler Projekte sammeln konnte.
Arne Limburg
Arne Limburg
Arne Limburg ist Softwarearchitekt bei der open knowledge GmbH in Oldenburg. Er verfügt über langjährige Erfahrung als Entwickler, Architekt und Consultant im Java-Umfeld und ist auch seit der ersten Stunde im Android-Umfeld aktiv.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: