Speicherauslastung optimieren

Die Speicheroptimierung ist entscheidend, um eine reibungslose Leistung zu gewährleisten, App-Abstürze zu verhindern und die Systemstabilität und Plattformintegrität aufrechtzuerhalten. Die Speichernutzung sollte in jeder App überwacht und optimiert werden. Inhalts-Apps für TV-Geräte haben jedoch besondere Herausforderungen, die sich von typischen Android-Apps für Mobilgeräte unterscheiden.

Ein hoher Arbeitsspeicherverbrauch kann zu Problemen mit dem Verhalten von Apps und Systemen führen, darunter:

  • Die App selbst kann langsam werden oder verzögert reagieren. Im schlimmsten Fall wird sie beendet.
  • Für Nutzer sichtbare Systemdienste (Lautstärkeregelung, Dashboard für Bildeinstellungen, Sprachassistent usw.) reagieren sehr langsam oder funktionieren überhaupt nicht.
  • Der Daemon-Prozess des Low-Memory-Killers (LMK) reagiert möglicherweise auf hohen Arbeitsspeicherbedarf, indem er die am wenigsten wichtigen Prozesse beendet. Diese Komponenten werden dann möglicherweise kurz darauf neu gestartet, was zu Spitzen bei der Ressourcenkonkurrenz führt, die sich direkt auf die App im Vordergrund auswirken können.
  • Der Übergang zum Launcher kann sich erheblich verzögern. Die im Vordergrund ausgeführte App reagiert dann möglicherweise nicht mehr, bis der Übergang abgeschlossen ist.
  • Das System kann mit der direkten Rückforderung beginnen und die Ausführung eines Threads vorübergehend unterbrechen, während auf die Arbeitsspeicherzuweisung gewartet wird. Das kann bei jedem Thread passieren, z. B. beim Hauptthread oder bei Codec-bezogenen Threads. Möglicherweise kommt es dadurch zu Audio- und Video-Frame-Drops und UI-Fehlern.

Speicheranforderungen auf TV-Geräten

Fernseher haben in der Regel deutlich weniger Arbeitsspeicher als Smartphones oder Tablets. Eine Konfiguration, die wir beispielsweise im Fernsehen sehen können, ist 1 GB RAM und eine Videoauflösung von 1080p. Gleichzeitig haben die meisten TV-Apps ähnliche Funktionen, was zu ähnlichen Implementierungen und gemeinsamen Herausforderungen führt. Diese beiden Situationen führen zu Problemen, die bei anderen Gerätetypen und Apps nicht auftreten:

  • Media-TV-Apps bestehen in der Regel sowohl aus Bildansichten in Rasterform als auch aus Vollbild-Hintergrundbildern, für die in kurzer Zeit viele Bilder in den Arbeitsspeicher geladen werden müssen.
  • TV-Apps spielen Multimedia-Streams ab, für die eine bestimmte Menge an Arbeitsspeicher zugewiesen werden muss, um Video und Audio abzuspielen. Außerdem benötigen sie erhebliche Medienpuffer, um eine reibungslose Wiedergabe zu gewährleisten.
  • Zusätzliche Mediafunktionen (z. B. Suche, Episodenwechsel, Änderung des Audiotracks) können bei unsachgemäßer Implementierung zu einer zusätzlichen Belastung des Arbeitsspeichers führen.

Informationen zu TV-Geräten

In diesem Leitfaden geht es hauptsächlich um die Arbeitsspeichernutzung von Apps und die Arbeitsspeicherziele für Geräte mit wenig RAM.

Bei TV-Geräten sind folgende Merkmale zu berücksichtigen:

  • Gerätespeicher: Die Größe des auf dem Gerät installierten RAM-Arbeitsspeichers (Random Access Memory).
  • Auflösung der Geräte-UI: Die Auflösung, die das Gerät zum Rendern der Benutzeroberfläche des Betriebssystems und der Apps verwendet. Sie ist in der Regel niedriger als die Videoauflösung des Geräts.
  • Videoauflösung: Die maximale Auflösung, mit der das Gerät Videos wiedergeben kann.

Daraus ergibt sich eine Kategorisierung verschiedener Gerätetypen und wie der Speicher von ihnen verwendet werden sollte.

Übersicht nach TV-Gerät

Gerätespeicher Videoauflösung des Geräts Auflösung der Geräte-Benutzeroberfläche isLowRAMDevice()
1 GB 1080p 720p Ja
1,5 GB 2.160 Pixel 1080p Ja
≥ 1,5 GB 1080p 720p oder 1080p Nein*
≥ 2 GB 2.160 Pixel 1080p Nein*

Fernseher mit wenig RAM

Diese Geräte haben wenig Arbeitsspeicher und melden ActivityManager.isLowRAMDevice() als „true“. Anwendungen, die auf Fernsehern mit wenig RAM ausgeführt werden, müssen zusätzliche Maßnahmen zur Speicherverwaltung implementieren.

Geräte mit den folgenden Merkmalen fallen in diese Kategorie:

  • Geräte mit 1 GB RAM: 1 GB RAM, Benutzeroberflächenauflösung von 720p/HD (1.280 × 720), Videoauflösung von 1.080p/FullHD (1.920 × 1.080)
  • Geräte mit 1,5 GB RAM: 1,5 GB RAM, Benutzeroberfläche mit einer Auflösung von 1080p/FullHD (1.920 × 1.080), Videoauflösung von 2160p/UltraHD/4K (3.840 × 2.160)
  • Andere Situationen, in denen der OEM das Flag ActivityManager.isLowRAMDevice() aufgrund zusätzlicher Arbeitsspeicherbeschränkungen definiert hat.

Normale Fernseher

Auf diesen Geräten kommt es nicht zu einer so starken Speicherauslastung. Wir gehen davon aus, dass diese Geräte die folgenden Merkmale aufweisen:

  • ≥ 1,5 GB RAM, 720p- oder 1080p-Benutzeroberfläche und 1080p-Videoauflösung
  • ≥ 2 GB RAM, 1080p-Benutzeroberfläche und 1080p- oder 2160p-Videoauflösung

Das bedeutet nicht, dass Apps auf diesen Geräten nicht auf die Speichernutzung achten sollten, da bestimmte Arten von Speichermissbrauch weiterhin zu einer Erschöpfung des verfügbaren Speichers und zu einer schlechten Leistung führen können.

Arbeitsspeicherziele auf Fernsehern mit wenig RAM

Wenn Sie den Arbeitsspeicher auf diesen Geräten messen, empfehlen wir dringend, jeden Abschnitt des Arbeitsspeichers mit dem Memory Profiler von Android Studio zu überwachen. TV-Apps sollten ihre Arbeitsspeichernutzung analysieren und darauf achten, dass die Kategorien unter den in diesem Abschnitt definierten Grenzwerten liegen.

Speicher-Profiler

Im Abschnitt So wird der Arbeitsspeicher gezählt finden Sie eine detaillierte Erklärung der gemeldeten Arbeitsspeicherzahlen. Bei der Definition von Grenzwerten für TV-Apps konzentrieren wir uns auf drei Speicherkategorien:

  • Anonym + Swap: Besteht aus Java + Native + Stack-Zuweisungsspeicher in Android Studio.
  • Grafiken: Direkt im Profiler-Tool gemeldet. Besteht in der Regel aus Grafiken.
  • Datei: In Android Studio als „Code“ + „Andere“ kategorisiert.

Anhand dieser Definitionen wird in der folgenden Tabelle der maximale Wert angegeben, der für die einzelnen Speichergruppentypen verwendet werden sollte:

Arbeitsspeichertyp Purpose Nutzungsziele (1 GB)
Anonym + Swap (Java + Native + Stack) Wird für Zuweisungen, Media-Puffer, Variablen und andere speicherintensive Aufgaben verwendet. < 160 MB
Grafik Wird von der GPU für Texturen und anzeigebezogene Puffer verwendet. 30–40 MB
Datei Wird für Codepages und Dateien im Speicher verwendet. 60–80 MB

Der maximale Gesamtarbeitsspeicher (Anon+Swap + Grafik + Datei) darf die folgenden Werte nicht überschreiten:

  • 280 MB Arbeitsspeichernutzung (Anon+Swap + Grafik + Datei) für Geräte mit 1 GB RAM.

Es wird dringend empfohlen, folgende Werte nicht zu überschreiten:

  • 200 MB Arbeitsspeichernutzung für (Anon+Swap + Grafik).

Dateispeicher

Allgemeine Hinweise für dateigestützten Arbeitsspeicher:

  • Im Allgemeinen wird der Dateispeicher vom Betriebssystem gut verwaltet.
  • Wir haben festgestellt, dass es derzeit keine Hauptursache für Arbeitsspeicherauslastung ist.

Wenn Sie jedoch allgemein mit dem Dateispeicher arbeiten, gilt Folgendes:

  • Nehmen Sie keine ungenutzten Bibliotheken in Ihren Build auf und verwenden Sie nach Möglichkeit kleine Teilmengen von Bibliotheken anstelle der vollständigen Bibliotheken.
  • Lassen Sie große Dateien nicht geöffnet, sondern schließen Sie sie, sobald Sie sie nicht mehr benötigen.
  • Minimieren Sie die Größe des kompilierten Codes für Java- und Kotlin-Klassen. Weitere Informationen finden Sie im Leitfaden App komprimieren, verschleiern und optimieren.

Konkrete TV-Empfehlungen

Dieser Abschnitt enthält spezifische Empfehlungen zur Optimierung der Arbeitsspeichernutzung auf TV-Geräten.

Grafikspeicher

Verwenden Sie geeignete Bildformate und ‑auflösungen.

  • Laden Sie keine Bilder mit einer höheren Auflösung als die Auflösung der Geräte-UI hoch. So sollten z. B. 1080p-Bilder auf einem Gerät mit einer Benutzeroberfläche mit 720p auf 720p verkleinert werden.
  • Verwenden Sie nach Möglichkeit hardwaregestützte Bitmaps.
    • Aktivieren Sie in Bibliotheken wie Glide das Feature Downsampler.ALLOW_HARDWARE_CONFIG, das standardmäßig deaktiviert ist. Wenn Sie diese Option aktivieren, werden Bitmaps nicht dupliziert, da sie sonst sowohl im Grafikspeicher als auch im anonymen Speicher vorhanden wären.
  • Zwischenrendern und erneutes Rendern vermeiden
    • Diese können mit dem Android GPU Inspector identifiziert werden:
    • Suchen Sie im Bereich „Textures“ nach Bildern, die Schritte auf dem Weg zum endgültigen Rendern darstellen und nicht nur die Elemente, aus denen sie bestehen. Dies ist in der Regel ein sogenanntes Zwischenrendering.
    • Bei Android SDK-Anwendungen können Sie diese häufig entfernen, indem Sie das Layout-Flag forceHasOverlappedRendering:false verwenden, um Zwischenrenderings für dieses Layout zu deaktivieren.
    • Weitere Informationen finden Sie unter Überlappende Renderings vermeiden.
  • Vermeiden Sie das Laden von Platzhalterbildern, verwenden Sie nach Möglichkeit @android:color/ oder @color für Platzhaltertexturen.
  • Vermeiden Sie das Zusammenfügen mehrerer Bilder auf dem Gerät, wenn dies auch offline möglich ist. Bevorzugen Sie das Laden von eigenständigen Bildern anstelle der Bildkomposition aus heruntergeladenen Bildern.
  • Hier findest du eine Anleitung zum Umgang mit Bitmaps.

Anonymer + Auslagerungsspeicher

Anon+Swap besteht aus nativen, Java- und Stack-Zuweisungen im Android Studio-Speicher-Profiler. Verwenden Sie ActivityManager.isLowMemoryDevice(), um zu prüfen, ob das Gerät über wenig Arbeitsspeicher verfügt, und passen Sie die App entsprechend an.

  • Medien:
    • Variable Größe für Media-Puffer angeben, abhängig vom RAM des Geräts und der Auflösung der Videowiedergabe. Das sollte 1 Minute Videowiedergabe umfassen:
      1. 40–60 MB für 1 GB / 1080p
      2. 60–80 MB für 1,5 GB / 1080p
      3. 80–100 MB für 1,5 GB / 2160p
      4. 100–120 MB für 2 GB / 2160p
    • Freigabe von Media-Speicherzuweisungen beim Ändern einer Folge, um eine Erhöhung der Gesamtmenge des anonymen Speichers zu verhindern.
    • Media-Ressourcen sofort freigeben und stoppen, wenn Ihre App beendet wird: Verwenden Sie die Lifecycle-Callbacks der Aktivität, um Audio- und Videoressourcen zu verarbeiten. Wenn Sie keine Audio-App sind, beenden Sie die Wiedergabe, wenn onStop() in Ihren Aktivitäten auftritt, speichern Sie alle Ihre Arbeit und legen Sie fest, dass Ihre Ressourcen freigegeben werden. Sie können Aufgaben planen, die Sie später benötigen. Weitere Informationen finden Sie im Abschnitt Jobs und Alarme.
    • Arbeitsspeicher des Puffers beim Suchen im Video im Blick behalten: Entwickler weisen beim Suchen oft zusätzliche 15–60 Sekunden an zukünftigen Inhalten zu, damit das Video für den Nutzer bereit ist. Dadurch entsteht jedoch zusätzlicher Arbeitsspeicher-Overhead. Im Allgemeinen sollten Sie nicht mehr als 5 Sekunden des zukünftigen Puffers verwenden, bis der Nutzer die neue Videoposition ausgewählt hat. Wenn du beim Suchen unbedingt zusätzliche Zeit vorpuffern musst, achte auf Folgendes:
      • Weisen Sie den Suchpuffer im Voraus zu und verwenden Sie ihn wieder.
      • Die Puffergröße sollte nicht größer als 15–25 MB sein (je nach Gerätespeicher).
  • Zuweisungen:
    • Hinweise zum Grafikspeicher helfen Ihnen dabei, keine Bilder im anonymen Speicher zu duplizieren.
      • Bilder beanspruchen oft den größten Teil des Speichers. Wenn sie dupliziert werden, kann das Gerät stark belastet werden. Das gilt insbesondere bei häufigem Navigieren in Bildrastern.
    • Zuweisungen von Releases aufheben, indem Sie ihre Referenzen beim Verschieben von Bildschirmen löschen: Achten Sie darauf, dass keine Referenzen zu Bitmaps und Objekten zurückbleiben.
  • Bibliotheken:
    • Profilspeicherzuweisungen aus Bibliotheken beim Hinzufügen neuer Bibliotheken, da sie möglicherweise auch zusätzliche Bibliotheken laden, was ebenfalls zu Zuweisungen und Bindungen führen kann.
  • Netzwerk:
    • Führen Sie beim Starten der App keine blockierenden Netzwerkaufrufe aus. Dadurch wird die Startzeit der App verlangsamt und es entsteht zusätzlicher Speicher-Overhead beim Start, wenn der Speicher durch die App-Last besonders eingeschränkt ist. Zeigen Sie zuerst einen Lade- oder Splash-Screen an und führen Sie Netzwerkanfragen erst aus, wenn die Benutzeroberfläche eingerichtet ist.

Bindungen

Bindungen führen zu zusätzlichem Speicherbedarf, da sie andere Anwendungen in den Arbeitsspeicher laden oder den Arbeitsspeicherverbrauch der gebundenen App erhöhen (wenn sie sich bereits im Arbeitsspeicher befindet), um den API-Aufruf zu ermöglichen. Dadurch wird der verfügbare Arbeitsspeicher für die Vordergrund-App reduziert. Achten Sie beim Binden eines Dienstes darauf, wann und wie lange Sie die Bindung verwenden. Lösen Sie die Bindung, sobald sie nicht mehr benötigt wird.

Typische Bindungen und Best Practices:

  • Play Integrity API: Wird verwendet, um die Geräteintegrität zu prüfen.
    • Geräteintegrität nach dem Ladebildschirm und vor der Medienwiedergabe prüfen
    • Geben Sie Verweise auf PlayIntegrity StandardIntegrityManager frei, bevor Sie Inhalte abspielen.
  • Play Billing Library: Wird zur Verwaltung von Abos und Käufen über Google Play verwendet.
  • GMS FontsProvider
    • Verwenden Sie auf Geräten mit wenig RAM lieber eigenständige Schriftarten als einen Schriftartenanbieter, da das Herunterladen der Schriftarten kostspielig ist und FontsProvider Dienste dafür binden wird.
  • Google Assistant-Bibliothek: Wird manchmal für die Suche und die In-App-Suche verwendet. Ersetzen Sie diese Bibliothek, wenn möglich.
    • Für Leanback-Apps: Verwenden Sie die Gboard-Sprachausgabe oder die androidx.leanback-Bibliothek.
      • Beachten Sie die Richtlinien für die Suche, wenn Sie die Suche implementieren.
      • Hinweis: Leanback wurde eingestellt. Apps sollten auf TV Compose umgestellt werden.
    • Für Compose-Apps:
      • Mit der Gboard-Sprachausgabe können Sie die Sprachsuche implementieren.
    • Implementieren Sie Als Nächstes ansehen, damit Medieninhalte in Ihrer App leichter gefunden werden können.

Dienste im Vordergrund

Dienste im Vordergrund sind eine spezielle Art von Dienst, der an eine Benachrichtigung gebunden ist. Diese Benachrichtigung wird auf Smartphones und Tablets in der Benachrichtigungsleiste angezeigt. TV-Geräte haben jedoch keine Benachrichtigungsleiste im gleichen Sinne wie diese Geräte. Auch wenn Dienste im Vordergrund nützlich sind, da sie weiter ausgeführt werden können, während sich die Anwendung im Hintergrund befindet, müssen TV-Apps die folgenden Richtlinien einhalten:

Auf Android TV und Google TV dürfen Dienste im Vordergrund nur weiter ausgeführt werden, wenn der Nutzer die App verlässt:

  • Für Audio-Apps:Vordergrunddienste dürfen nur ausgeführt werden, wenn der Nutzer die App verlässt, um den Audiotrack weiter abzuspielen. Der Dienst muss sofort nach Beendigung der Audiowiedergabe beendet werden.
  • Für alle anderen Apps:Alle Dienste im Vordergrund müssen beendet werden, sobald der Nutzer Ihre App verlässt, da es keine Benachrichtigung gibt, die den Nutzer darüber informiert, dass die App weiterhin ausgeführt wird und Ressourcen verbraucht.
  • Verwende für Hintergrundjobs wie das Aktualisieren von Empfehlungen oder Als Nächstes ansehen WorkManager.

Jobs und Wecker

WorkManager ist die moderne Android-API zum Planen von wiederkehrenden Hintergrundjobs. WorkManager verwendet das neue JobScheduler, sofern verfügbar (SDK 23+), und das alte AlarmManager, wenn nicht. Hier sind einige Best Practices für die Ausführung geplanter Jobs auf dem Fernseher:

  • Vermeiden Sie die Verwendung der AlarmManager APIs auf SDK 23 und höher, insbesondere AlarmManager.set(), AlarmManager.setExact() und ähnliche Methoden, da das System so nicht entscheiden kann, wann die Jobs ausgeführt werden sollen (z. B. wenn sich das Gerät im Leerlauf befindet).
  • Auf Geräten mit wenig RAM sollten Sie Jobs nur ausführen, wenn es unbedingt erforderlich ist. Verwende WorkManager WorkRequest bei Bedarf nur zum Aktualisieren von Empfehlungen nach der Wiedergabe und versuche, dies zu tun, während die App noch geöffnet ist.
  • Definieren Sie Constraints für WorkManager, damit das System Ihre Jobs zur richtigen Zeit ausführt:

Kotlin

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

Java

Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED)
    .setRequiresStorageNotLow(true)
    .setRequiresDeviceIdle(true)
    .build()
  • Wenn du Jobs regelmäßig ausführen musst, z. B. um Als Nächstes ansehen basierend auf den Inhalten, die ein Nutzer in deiner App auf einem anderen Gerät angesehen hat, zu aktualisieren, solltest du den Arbeitsspeicherverbrauch des Jobs unter 30 MB halten.

Allgemeine Hinweise zum Arbeitsspeicher

Die folgenden Richtlinien enthalten allgemeine Informationen zur Entwicklung von Android-Apps:

  • Minimieren Sie die Objektzuweisungen, optimieren Sie die Wiederverwendung von Objekten und geben Sie nicht verwendete Objekte umgehend frei.
    • Keine Referenzen auf Objekte, insbesondere Bitmaps, beibehalten.
    • Vermeiden Sie die Verwendung von System.gc() und direkten Speicherfreigabeaufrufen, da sie den Speicherverwaltungsprozess des Systems beeinträchtigen. Auf Geräten, die zRAM verwenden, kann ein erzwungener Aufruf von gc() beispielsweise die Arbeitsspeichernutzung aufgrund der Komprimierung und Dekomprimierung des Speichers vorübergehend erhöhen.
    • Verwenden Sie LazyList, wie in einem Katalogbrowser in Compose oder RecyclerView im jetzt eingestellten Leanback-UI-Toolkit gezeigt, um Ansichten wiederzuverwenden und Listenelemente nicht neu zu erstellen.
    • Speichern Sie Elemente, die von externen Content-Anbietern gelesen werden und sich wahrscheinlich nicht ändern, lokal im Cache und definieren Sie Aktualisierungsintervalle, die die Zuweisung von zusätzlichem externen Speicher verhindern.
  • Auf mögliche Speicherlecks prüfen
    • Achte auf typische Fälle von Speicherlecks, z. B. auf Referenzen in anonymen Threads, Neuzuweisung von Videopuffern, die nie freigegeben werden, und ähnliche Situationen.
    • Verwenden Sie Heap-Dumps, um Speicherlecks zu beheben.
  • Erstellen Sie Baseline-Profile, um die Menge an Just-in-Time-Kompilierung zu minimieren, die beim Kaltstart Ihrer App erforderlich ist.

Direkte Arbeitsspeicherfreigabe

Wenn eine Android TV-Anwendung Arbeitsspeicher anfordert und das System überlastet ist, muss der Linux-Kernel, der Android zugrunde liegt, möglicherweise Direct Memory Reclaim verwenden.

Dabei wird jeder Zuweisungsthread vollständig pausiert, um auf freigegebene Speicherseiten zu warten. Dies tritt auf, wenn durch die Hintergrundbereinigung nicht proaktiv ein ausreichender Arbeitsspeicherpool aufrechterhalten werden kann.

Dies kann zu merklichen Pausen oder Verzögerungen bei der Nutzererfahrung führen, da das System die Zuweisung von Threads pausiert, bis genügend Arbeitsspeicher verfügbar ist. In diesem Sinne ist die Zuweisung von Threads nicht auf Aufrufe von Anwendungscode wie malloc() beschränkt. Speicher muss beispielsweise für das Paging von Code-Seiten zugewiesen werden.

Übersicht über die Tools