Cache für Apps

Android 11 (API‑Level 30) oder höher unterstützt das Einfrieren von Apps im Cache. Diese Funktion beendet die Ausführung für Prozesse im Cache und reduziert die Ressourcennutzung durch fehlerhaftes Verhalten von Apps, die möglicherweise versuchen, im Cache zu arbeiten.

Der Cache-Apps-Freezer hält Apps im RAM, ohne die CPU zu belasten. Wenn Android feststellt, dass eine App keine Aufgaben ausführen sollte, aber möglicherweise in Zukunft benötigt wird, wird der App-Prozess eingefroren, anstatt beendet. So wird ein Kaltstart verhindert, wenn die App wieder benötigt wird.

Android friert im Cache gespeicherte Apps ein, indem die zugehörigen Prozesse in eine eingefrorene Cgroup migriert werden. Dadurch wird die CPU-Nutzung im aktiven und im Leerlaufmodus bei aktiven gecachten Apps reduziert. Sie können die App-Einfrierung über ein Systemkonfigurationsflag oder eine Entwickleroption aktivieren.

In Android 14 (API‑Level 34) und höher bietet der Cache-Apps-Freezer die folgenden robusten Verhaltensweisen:

  • App-Prozesse im Cache-Zustand werden 10 Sekunden nach dem Eintreten in den Cache-Zustand eingefroren.
  • Das System friert einen eingefrorenen App-Prozess während eines Lebenszyklusereignisses sofort wieder auf. Zu diesen Ereignissen gehören der Empfang eines Intents, der Start eines Job-Dienstes oder die Wiederaufnahme einer Aktivität durch den Nutzer.

ActivityManagerService verwaltet alle App-Prozesse und trifft Entscheidungen zum App-Lebenszyklus. CachedAppOptimizer ist für das Einfrieren des App-Prozesses verantwortlich.

Wenn ein App-Prozess eingefroren wird, werden alle seine Threads angehalten und können erst wieder CPU-Arbeit ausführen, wenn sie wieder aktiviert werden. Daher kann die App keine Garbage Collection (GC) durchführen und nicht auf Ereignisse zum Speicherkürzen reagieren. Weitere Informationen finden Sie unter ComponentCallbacks2.onTrimMemory(int). Ab Android 14 gilt Folgendes:

  • Apps mit einer sichtbaren Activity-Instanz werden über TRIM_MEMORY_UI_HIDDEN benachrichtigt, sobald sie in den Hintergrund verschoben werden. Apps, die sich in einem Lebenszyklus ohne Benutzeroberfläche befinden, z. B. Apps mit einem Dienst im Vordergrund, erhalten möglicherweise TRIM_MEMORY_BACKGROUND. Andere Ereignisse zum Kürzen werden nicht gesendet, da Apps, die für diese Ereignisse infrage kommen, eingefroren werden sollten.
  • Kurz nach dem Wechsel in den Cache-Status fordert das System möglicherweise die App-Laufzeit auf, eine Garbage Collection durchzuführen, um sich auf das Einfrieren vorzubereiten.
  • Wenn ein App-Prozess eingefroren wird, können zusätzliche Schritte zur Speicherverdichtung erfolgen, z. B. das Schreiben von „dirty“ Pages in den Sicherungsspeicher und das Auslagern anonymer Pages in ZRAM.
  • Wenn alle Prozesse für eine bestimmte App eingefroren sind, beendet das System alle aktiven TCP-Sockets, die von der App verwaltet werden. Dadurch wird verhindert, dass die Serverseite des Sockets TCP-Keep-Alive-Pings sendet, die das Modem des Geräts aktivieren würden.

Prozesse von Apps im Cache werden reaktiviert, wenn ihr Prozessstatus von „im Cache“ zu einem Status mit höherer Priorität wechselt. Um das Auftauen von Apps in Android 14 und höher zu reduzieren, stellt das System kontextregistrierte Broadcasts in die Warteschlange, während sich die App im Cache-Status befindet. Kontextregistrierte Broadcasts sind Empfänger, die von einer App dynamisch durch Aufrufen von Context.registerReceiver registriert werden. Diese in die Warteschlange gestellten Broadcasts werden erst ausgeliefert, wenn die App wieder aktiviert wird. Im Gegensatz dazu werden im System keine im Manifest deklarierten Broadcasts in die Warteschlange gestellt. Im Manifest deklarierte Broadcasts sind Empfänger, die in AndroidManifest.xml mit dem Element <receiver> statisch deklariert werden. Das System friert die im Cache gespeicherte App sofort wieder ein, um im Manifest deklarierte Broadcasts zu senden.

Auswirkungen auf den Systemzustand

Android beendet den Prozess der im Cache gespeicherten App, deren letzte Verwendung am längsten zurückliegt, wenn mehr als MAX_CACHED_PROCESSES Prozesse im Cache gespeichert sind. Auf unterstützten Geräten mit Android 14 oder höher wird MAX_CACHED_PROCESSES deutlich erhöht, sodass Geräte wesentlich mehr zwischengespeicherte App-Prozesse im RAM behalten können.

Wenn mehr Apps im RAM zwischengespeichert werden, können Kaltstarts um bis zu 30% reduziert werden. Die Reduzierung skaliert dabei mit dem gesamten RAM des Geräts. Gleichzeitig wird der CPU-Verbrauch durch im Cache gespeicherte Apps minimiert, was zu einer erheblichen Akkuschonung führt.

Ausnahmen für Gefrierschränke

Unter bestimmten Bedingungen kann ein App-Prozess in den Cache-Status wechseln, ohne eingefroren zu werden. Diese Ausnahmen sind Implementierungsdetails und können sich in zukünftigen Android-Versionen ändern:

  • Dateisperren:Wenn ein im Cache gespeicherter Prozess eine Dateisperre enthält, die andere nicht im Cache gespeicherte Prozesse blockiert, wird der Prozess, der die Sperre enthält, nicht eingefroren.
  • BIND_WAIVE_PRIORITY-Bindungen:App-Prozesse mit eingehenden Bindungen, die mit Context.BIND_WAIVE_PRIORITY erstellt wurden, können in den Cache-Status wechseln, bleiben aber eingefroren, bis alle verbundenen Clientprozesse ebenfalls im Cache sind. Diese Ausnahme unterstützt Apps mit mehreren Prozessen, z. B. Webbrowser, die benutzerdefinierte Tabs verwenden.

App-Gefrierschrank implementieren

Der Freezer für zwischengespeicherte Apps nutzt den Kernel-Freezer für cgroup v2. Auf Geräten mit einem kompatiblen Kernel kann die Funktion aktiviert werden. Aktivieren Sie die Entwickleroption Ausführung für im Cache gespeicherte Apps anhalten oder legen Sie das Gerätekonfigurationsflag activity_manager_native_boot use_freezer auf true fest. Beispiel:

adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot

Der Freezer ist deaktiviert, wenn Sie das Flag use_freezer auf false setzen oder die Entwickleroption deaktivieren. Beispiel:

adb shell device_config put activity_manager_native_boot use_freezer false && adb reboot

Sie können diese Einstellung ändern, indem Sie eine Gerätekonfiguration in einer Softwareversion oder einem Update ändern.

So überschreiben Sie MAX_CACHED_PROCESSES, um den Wert beispielsweise für Tests auf 1024 zu setzen:

adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistent

So machen Sie die MAX_CACHED_PROCESSES-Überschreibung rückgängig:

adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none

Die Apps Freezer-Funktion stellt keine offiziellen APIs zur Verfügung und hat keinen Referenzimplementierungsclient. Sie verwendet jedoch die verborgenen System-APIs setProcessFrozen, um einen einzelnen Prozess einzufrieren, und enableFreezer, um das Einfrieren global zu aktivieren oder zu deaktivieren.

Benutzerdefinierte Funktionen verarbeiten

App-Prozesse sollten im Cache nicht ausgeführt werden. Einige Apps haben jedoch möglicherweise benutzerdefinierte Funktionen, die von Prozessen unterstützt werden, die im Cache ausgeführt werden sollen. Wenn die App-Gefriertruhe auf einem Gerät aktiviert ist, auf dem solche Apps ausgeführt werden, werden die im Cache gespeicherten Prozesse eingefroren. Das kann dazu führen, dass benutzerdefinierte Funktionen nicht mehr funktionieren.

Als Workaround können Sie den Prozessstatus auf „Nicht im Cache“ ändern, bevor der Prozess eine Aufgabe ausführen muss. Durch diese Änderung können Apps aktiv bleiben. Beispiele für aktive Status sind ein gebundener Dienst im Vordergrund oder der Vordergrundstatus.

Häufige Fehlermodi

Wenn App-Prozesse eingefroren werden, kann eine unsachgemäße Interprozesskommunikation (IPC) oder Aufgabenplanung zu App-Beendigungen oder unerwartetem Verhalten führen.

Synchrone Binder-Transaktionen für eingefrorene Prozesse

Wenn ein Client-App-Prozess eine synchrone Binder-Transaktion an einen eingefrorenen Server-App-Prozess sendet, beendet das System den Server-App-Prozess sofort. So wird verhindert, dass der Client-Thread unbegrenzt blockiert wird, während er auf eine Antwort vom eingefrorenen Server wartet. Der Client-Thread empfängt dann RemoteException und alle registrierten Listener werden ausgelöst. Weitere Informationen finden Sie unter IBinder.linkToDeath.

Ursache:Dieser Fehler wird in der Regel durch einen Fehler in der Client-App verursacht. Wenn ein Client an einen Dienst gebunden ist, ist der Serverprozess an den Client gebunden und kann nicht in den Cache-Zustand eintreten, bevor der Client dies tut. Weitere Informationen finden Sie unter Context.bindService. Sobald der Client jedoch Context.unbindService aufruft, kann der Serverprozess im Cache gespeichert und eingefroren werden. Wenn der Client die im Cache gespeicherte IBinder-Referenz nach dem Aufheben der Bindung weiterhin verwendet, riskiert er die Kommunikation mit einem eingefrorenen Prozess.

Um dieses Problem zu vermeiden, müssen Client-Apps IBinder-Referenzen sofort nach dem Aufrufen von Context.unbindService verwerfen.

Asynchroner Binder-Transaktionspufferüberlauf

Wenn ein Server-App-Prozess während des Einfrierens asynchrone (oneway) Binder-Transaktionen empfängt, werden die Transaktionen in einem prozessbezogenen Puffer zwischengespeichert. Wenn der Server im eingefrorenen Zustand zu viele asynchrone Transaktionen empfängt, läuft der Puffer über und das System beendet den Server-App-Prozess.

Um diesen Pufferüberlauf zu verhindern, sollten Sie nicht zu viele asynchrone Binder-Transaktionen an Prozesse senden, die möglicherweise im Cache gespeichert oder eingefroren werden.

Wiederholte Ausführung geplanter Aufgaben nach dem Reaktivieren

Wenn eine App sich wiederholende Aufgaben ausführt, werden diese angehalten, während der Prozess eingefroren ist. Weitere Informationen finden Sie unter ScheduledThreadPoolExecutor.scheduleAtFixedRate oder Timer.scheduleAtFixedRate. Wenn der Prozess wieder aktiv ist, werden die angehäuften verpassten Ausführungen möglicherweise schnell hintereinander ohne nennenswerte Verzögerung ausgeführt.

Um einen Anstieg der Ausführungen zu verhindern, wenn die App wieder aktiviert wird, verwenden Sie für Hintergrundaufgaben scheduleWithFixedDelay anstelle von scheduleAtFixedRate. Sie können aber auch WorkManager verwenden.

App-Freezer testen und Fehler beheben

Wenn Sie prüfen möchten, ob die App-Einfrierung wie vorgesehen funktioniert, oder Probleme mit der Einfrierung beheben möchten, verwenden Sie die folgenden Diagnosetools und Befehle:

Befehle für den Aktivitätsmanager

Mit adb shell am-Befehlen können Sie das Einfrieren und Verdichten für einen bestimmten Prozess manuell steuern:

  • Prozess zum Einfrieren zwingen:

    adb shell am freeze <process>
  • Einen Prozess zum Entfrosten zwingen:

    adb shell am unfreeze <process>
  • Vollständige Arbeitsspeicherverdichtung für einen Prozess erzwingen:

    adb shell am compact full <process>

Logcat-Prüfung

Mit logcat können Sie eingefrorene und nicht eingefrorene Einträge sehen, wenn ein Prozess in den Freezer migriert oder aus dem Freezer migriert wird:

adb logcat | grep -i "\(freezing\|froze\)"

In den Logs für den Grund für das Aufheben der Einfrierung werden aufgezählte Werte aus dem Enum des UnfreezeReason-Protokollzwischenspeichers ausgegeben.

Dumpsys-Prüfung

Mit dumpsys activity können Sie nach einer Liste eingefrorener Prozesse suchen:

adb shell dumpsys activity | grep -A 20 "Apps frozen:"

Prüfen Sie, ob die Datei /sys/fs/cgroup/uid_0/cgroup.freeze vorhanden ist.

ApplicationExitInfo

Wenn Sie den Grund für die Beendigung eines vorherigen Prozesses abfragen möchten, lesen Sie den Abschnitt ActivityManager.getHistoricalProcessExitReasons. Wenn ein App-Prozess aufgrund eines Freezer-Problems beendet wurde, z. B. weil er eine synchrone Binder-Transaktion erhalten hat, während er eingefroren war, wird der Beendigungsgrund auf ApplicationExitInfo.REASON_FREEZER gesetzt.

Perfetto-Tracing

Ereignisse im Zusammenhang mit dem Freezer werden in Perfetto-Traces für den Prozess system_server in einem Track namens Freezer ausgegeben:

  • Die Segmente Freeze und Unfreeze geben an, wann sich der Status eines Prozesses ändert.
  • updateAppFreezeStateLSP-Ereignisse geben an, wann der Systemserver Prozessattribute noch einmal prüft, um Entscheidungen zum Einfrieren oder Aufheben des Einfrierens zu treffen.

Sie können diese Ereignisse direkt in der Perfetto-Benutzeroberfläche ansehen oder mit PerfettoSQL analysieren:

INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");

In der PerfettoSQL-Standardbibliothek werden Freezer-Ereignisse auch in der Tabelle android_freezer_events zusammengefasst.