Speicherauslastung optimieren

Die Arbeitsspeicheroptimierung ist entscheidend, um eine reibungslose Leistung zu gewährleisten, App-Abstürze zu verhindern und die Systemstabilität und die Integrität der Plattform 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 oder im schlimmsten Fall beendet werden.
  • Für Nutzer sichtbare Systemdienste (Lautstärkeregelung, Dashboard für Bildeinstellungen, Sprachassistent usw.) reagieren sehr langsam oder funktionieren möglicherweise gar nicht.
  • Der Daemon-Prozess des Low-Memory-Killers (LMK) reagiert möglicherweise auf eine hohe Arbeitsspeicherauslastung, indem er die am wenigsten wichtigen Prozesse beendet. Diese Komponenten werden dann möglicherweise kurz darauf neu gestartet, was zu Spitzen weiterer Ressourcenkonflikte 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 erst wieder, wenn 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. Dadurch kann es zu Audio- und Video-Frame-Drops und UI-Fehlern kommen.

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 einer ähnlichen Implementierung und ähnlichen 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 zum Abspielen von Video und Audio zugewiesen werden muss. Außerdem sind erhebliche Medienpuffer erforderlich, um eine reibungslose Wiedergabe zu gewährleisten.
  • Zusätzliche Mediafunktionen (z. B. Suche, Episodenwechsel, Änderung des Audiotracks) können zusätzlichen Arbeitsspeicher beanspruchen, wenn sie nicht richtig implementiert werden.

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.

Auf TV-Geräten sollten Sie Folgendes berücksichtigen:

  • Gerätespeicher: Die Größe des auf dem Gerät installierten RAM-Arbeitsspeichers (Random Access Memory).
  • Auflösung der Geräte-Benutzeroberfläche: 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 Videos auf dem Gerät wiedergegeben werden können.

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äche mit einer Auflösung von 720p/HD (1280 × 720), Videoauflösung von 1080p/FullHD (1920 × 1080)
  • 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 2.160p/UltraHD/4K (3.840 × 2.160)
  • Andere Situationen, in denen der OEM das Flag ActivityManager.isLowRAMDevice() aufgrund zusätzlicher Arbeitsspeicherbeschränkungen definiert hat.

Normale Fernsehgeräte

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

  • Mindestens 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 Arbeitsspeichernutzung 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 Speicher-Profiler von Android Studio zu überwachen. TV-Apps sollten ihre Arbeitsspeichernutzung analysieren und darauf achten, dass die Kategorien unter den Schwellenwerten liegen, die wir in diesem Abschnitt definieren.

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 Allocation Memory in Android Studio.
  • Grafiken: Direkt im Profiler-Tool. 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 Speichergruppen 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 Arbeitsspeicher verwendet. 60–80 MB

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

  • 280 MB Arbeitsspeichernutzung insgesamt (Anon+Swap + Graphics + File) für Geräte mit 1 GB Low-RAM.

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

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

Dateispeicher

Allgemeine Hinweise zum 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 im Arbeitsspeicher geöffnet und 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. So sollten beispielsweise 1080p-Bilder auf einem Gerät mit einer 720p-Benutzeroberfläche 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-Prüfer identifiziert werden:
    • Suchen Sie im Bereich „Texturen“ nach Bildern, die Schritte 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, wenn möglich. Verwenden Sie @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 gegenüber der Bildkomposition aus heruntergeladenen Bildern.
  • Hier findest du eine Anleitung zum Umgang mit Bitmaps.

Anon+Swap-Speicher

Anon+Swap besteht aus nativen, Java- und Stack-Zuweisungen im Android Studio-Speicherprofiler. Verwenden Sie ActivityManager.isLowMemoryDevice(), um zu prüfen, ob das Gerät über wenig Arbeitsspeicher verfügt, und passen Sie die App entsprechend dieser Situation 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 einer Minute Videowiedergabe entsprechen:
      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 des gesamten 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 Benachrichtigungen.
    • Arbeitsspeicher des Zwischenspeichers 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-Aufwand. 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 Arbeitsspeichers. Wenn sie dupliziert werden, kann das Gerät stark belastet werden. Das gilt insbesondere bei häufiger Navigation in Bildrastern.
    • Zuweisungen freigeben, indem Sie ihre Referenzen beim Wechseln 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 App-Start 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 vorhanden ist.

Bindungen

Bindungen führen zu zusätzlichem Speicher-Overhead, da sie andere Anwendungen in den Arbeitsspeicher laden oder den Speicherverbrauch der gebundenen App erhöhen (wenn sie bereits im Arbeitsspeicher ist), um den API-Aufruf zu ermöglichen. Dadurch wird der verfügbare Arbeitsspeicher für die App im Vordergrund reduziert. Achten Sie beim Binden eines Dienstes darauf, wann und wie lange Sie die Bindung verwenden. Geben Sie die Bindung frei, 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 Watch Next, um Medieninhalte in Ihrer App auffindbar zu machen.

Dienste im Vordergrund

Dienste im Vordergrund sind eine spezielle Art von Dienst, der mit einer Benachrichtigung verknüpft ist. Diese Benachrichtigung wird auf Smartphones und Tablets in der Benachrichtigungsleiste angezeigt. TV-Geräte haben jedoch keine Benachrichtigungsleiste im selben 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:Dienste im Vordergrund dürfen nur weiter 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 dadurch 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. Verwenden Sie WorkManager WorkRequest bei Bedarf nur zum Aktualisieren von Empfehlungen nach der Wiedergabe und versuchen Sie, 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 der Content-Wiedergabeaktivität eines Nutzers in deiner App auf einem anderen Gerät 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 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 mit zRAM kann beispielsweise ein erzwungener Aufruf von gc() die Arbeitsspeichernutzung aufgrund der Komprimierung und Dekomprimierung des Speichers vorübergehend erhöhen.
    • Verwende 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.
  • Prüfen Sie, ob es möglicherweise Speicherlecks gibt.
    • Achte auf typische Fälle von Speicherlecks, z. B. auf Referenzen in anonymen Threads, Neuzuweisung von Videopuffern, die nie freigegeben werden, und ähnliche Situationen.
    • Mit einem Heap-Dump können Sie Speicherlecks beheben.
  • Erstellen Sie Baseline-Profile, um die Menge an Just-in-Time-Kompilierung zu minimieren, die beim Ausführen Ihrer App bei einem Kaltstart 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. Das passiert, 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 Anwendungsaufrufe wie malloc() beschränkt. Speicher muss beispielsweise für das Paging von Code-Seiten zugewiesen werden.

Übersicht über Tools