Prozesse und App-Lebenszyklus

In den meisten Fällen wird jede Android-App in einem eigenen Linux-Prozess ausgeführt. Dieser Prozess wird für die Anwendung erstellt, wenn ein Teil des Codes ausgeführt werden muss und weiter ausgeführt wird, bis das System seinen Arbeitsspeicher zur Verwendung durch andere Anwendungen zurückfordern muss und nicht mehr benötigt wird.

Eine ungewöhnliche und grundlegende Funktion von Android besteht darin, dass die Lebensdauer eines Anwendungsprozesses nicht direkt von der App selbst gesteuert wird. Stattdessen wird dies vom System durch eine Kombination der Teile der Anwendung, von denen das System weiß, dass sie ausgeführt werden, bestimmt, wie wichtig diese Dinge für den Nutzer sind und wie viel Gesamtspeicher im System verfügbar ist.

Anwendungsentwickler müssen wissen, wie sich verschiedene Anwendungskomponenten (insbesondere Activity, Service und BroadcastReceiver) auf die Lebensdauer des Anwendungsprozesses auswirken. Wenn diese Komponenten nicht korrekt verwendet werden, kann das dazu führen, dass das System den Anwendungsprozess beendet, während diese wichtige Arbeit ausführt.

Ein gängiges Beispiel für einen Fehler im Prozesslebenszyklus ist ein BroadcastReceiver, der einen Thread startet, wenn er ein Intent in seiner BroadcastReceiver.onReceive()-Methode empfängt, und dann von der Funktion zurückkehrt. Nach der Rückgabe erkennt das System die BroadcastReceiver als nicht mehr aktiv und der Hostingprozess wird nicht mehr benötigt, sofern keine anderen Anwendungskomponenten darin aktiv sind.

Das System kann den Prozess also jederzeit beenden, um Arbeitsspeicher freizugeben. Dadurch wird der erstellte Thread beendet, der während des Prozesses ausgeführt wird. Die Lösung für dieses Problem besteht in der Regel darin, einen JobService aus der BroadcastReceiver zu planen, damit das System erkennt, dass im Prozess aktive Arbeit ausgeführt wird.

Um zu bestimmen, welche Prozesse bei niedrigem Arbeitsspeicher beendet werden sollen, ordnet Android jeden Prozess einer Wichtigkeitshierarchie zu, die auf den darin ausgeführten Komponenten und dem Status dieser Komponenten basiert. Diese Prozesstypen sind nach ihrer Wichtigkeit geordnet:

  1. Ein Hintergrundprozess ist ein Prozess, der für das, was der Nutzer gerade tut, erforderlich ist. Verschiedene Anwendungskomponenten können dazu führen, dass der enthaltende Prozess auf unterschiedliche Weise als Vordergrund behandelt wird. Ein Prozess wird als im Vordergrund ausgeführt, wenn eine der folgenden Bedingungen erfüllt ist:
  2. Es gibt nur wenige solcher Prozesse im System und diese werden nur als letzte Option beendet, wenn der Arbeitsspeicher so gering ist, dass selbst diese Prozesse nicht mehr ausgeführt werden können. Im Allgemeinen hat das Gerät in diesem Fall einen Speicher-Paging-Status erreicht. Diese Aktion ist also erforderlich, damit die Benutzeroberfläche responsiv bleibt.

  3. Ein sichtbarer Prozess führt eine Arbeit aus, die dem Nutzer bekannt ist, sodass es deutlich negative Auswirkungen auf die Nutzererfahrung hat, wenn man ihn abbricht. Ein Prozess gilt unter folgenden Bedingungen als sichtbar:
    • Er führt ein Activity aus, das für den Nutzer auf dem Bildschirm, aber nicht im Vordergrund sichtbar ist (seine onPause()-Methode wurde aufgerufen). Das kann beispielsweise vorkommen, wenn die Vordergrund-Activity als Dialogfeld angezeigt wird, damit die vorherige Activity dahinter sichtbar ist.
    • Es hat einen Service, der über Service.startForeground() als Dienst im Vordergrund ausgeführt wird. Dieser wird vom System aufgefordert, den Dienst als etwas zu behandeln, was dem Nutzer bekannt ist, oder im Wesentlichen so, als wäre er sichtbar.
    • Es hostet einen Dienst, den das System für eine bestimmte Funktion verwendet, die dem Nutzer bekannt ist, z. B. einen Live-Hintergrund oder einen Eingabemethodendienst.

    Die Anzahl dieser Prozesse, die im System ausgeführt werden, ist weniger begrenzt als die Prozesse im Vordergrund, aber sie wird dennoch relativ kontrolliert. Diese Prozesse gelten als extrem wichtig und werden nur beendet, wenn dies nicht erforderlich ist, um alle Prozesse im Vordergrund weiter auszuführen.

  4. Ein Dienstprozess enthält eine Service, die mit der Methode startService() gestartet wurde. Obwohl diese Prozesse für den Nutzer nicht direkt sichtbar sind, werden in der Regel Dinge ausgeführt, die dem Nutzer wichtig sind, z. B. das Hoch- und Herunterladen von Netzwerkdaten im Hintergrund. Das System hält solche Prozesse also immer aktiv, es sei denn, nicht genügend Arbeitsspeicher vorhanden, um alle Prozesse im Vordergrund und alle sichtbaren Prozesse aufzubewahren.

    Dienste, die über einen längeren Zeitraum ausgeführt wurden (z. B. 30 Minuten oder länger), werden möglicherweise herabgestuft, damit der Prozess auf die im Cache gespeicherte Liste gesetzt wird.

    Prozesse, die über einen längeren Zeitraum ausgeführt werden müssen, können mit setForeground erstellt werden. Wenn es sich um einen regelmäßigen Prozess handelt, der eine exakte Ausführungszeit erfordert, kann er über AlarmManager geplant werden. Weitere Informationen finden Sie unter Unterstützung für Worker mit langer Ausführungszeit. So lassen sich Situationen vermeiden, in denen lang andauernde Dienste, die übermäßig viele Ressourcen nutzen (z. B. durch Speicherlecks), verhindern, dass das System eine gute Nutzererfahrung bietet.

  5. Ein im Cache gespeicherter Prozess ist ein Prozess, der derzeit nicht benötigt wird. Daher kann das System ihn bei Bedarf beenden, wenn Ressourcen wie Arbeitsspeicher an anderer Stelle benötigt werden. In einem normal funktionierenden System sind dies die einzigen Prozesse, die für die Ressourcenverwaltung erforderlich sind.

    In einem gut laufenden System sind mehrere im Cache gespeicherte Prozesse immer verfügbar, was einen effizienten Wechsel zwischen Anwendungen ermöglicht. Die im Cache gespeicherten Anwendungen werden bei Bedarf regelmäßig beendet. Nur in sehr kritischen Situationen erreicht das System einen Punkt, an dem alle im Cache gespeicherten Prozesse beendet werden und Dienstprozesse damit beendet werden müssen.

    Da im Cache gespeicherte Prozesse jederzeit vom System abgebrochen werden können, sollten die Anwendungen, die sich im Cache befinden, nicht mehr ausgeführt werden. Wenn nutzerkritische Aufgaben von der Anwendung ausgeführt werden müssen, sollten die oben genannten APIs verwendet werden, um Arbeiten im aktiven Prozessstatus auszuführen.

    Prozesse im Cache enthalten oft eine oder mehrere Activity-Instanzen, die derzeit für den Nutzer nicht sichtbar sind (die Methode onStop() wurde aufgerufen und zurückgegeben). Wenn der Activity-Lebenszyklus korrekt implementiert wird, wenn das System solche Prozesse beendet, hat dies keine Auswirkungen auf den Nutzer, wenn er zu dieser App zurückkehrt. Der zuvor gespeicherte Status kann wiederhergestellt werden, wenn die zugehörige Aktivität in einem neuen Prozess neu erstellt wird. Beachten Sie, dass onDestroy() nicht in jedem Fall aufgerufen wird, wenn ein Prozess vom System abgebrochen wird. Weitere Informationen findest du unter Activity.

    Ab Android 13 erhält ein App-Prozess möglicherweise nur eine begrenzte oder gar keine Ausführungszeit, bis er einen der oben genannten aktiven Lebenszyklusstatus erreicht.

    Im Cache gespeicherte Prozesse werden in einer Liste geführt. Die genaue Bestellrichtlinie für diese Liste ist ein Implementierungsdetail der Plattform. Im Allgemeinen werden Prozesse, die nützlicher sind, z. B. Prozesse, die die Home-App des Nutzers hosten, oder die letzte Aktivität, die der Nutzer gesehen hat, vor anderen Prozesstypen erhalten. Es können auch andere Richtlinien zum Beenden von Prozessen angewendet werden, z. B. harte Limits für die Anzahl der zulässigen Prozesse oder die Begrenzung der Zeit, die ein Prozess kontinuierlich im Cache gespeichert bleiben kann.

Bei der Entscheidung, wie ein Prozess klassifiziert werden soll, stützt das System die Entscheidung auf die wichtigste Ebene unter allen derzeit im Prozess aktiven Komponenten. In der Dokumentation zu Activity, Service und BroadcastReceiver finden Sie weitere Informationen dazu, wie jede dieser Komponenten zum Gesamtlebenszyklus eines Prozesses und der Anwendung beiträgt.

Die Priorität eines Prozesses kann auch aufgrund anderer Abhängigkeiten erhöht werden, die für einen Prozess gelten. Wenn beispielsweise Prozess A mit dem Flag Context.BIND_AUTO_CREATE an eine Service gebunden wurde oder in Prozess B eine ContentProvider verwendet, ist die Klassifizierung von Prozess B immer mindestens so wichtig wie die von Prozess A.