Freigeben über


Aktivitätslebenszyklus

Aktivitäten sind ein grundlegender Baustein von Android-Anwendungen und können in verschiedenen Zuständen vorhanden sein. Der Aktivitätslebenszyklus beginnt mit Instanziierung und endet mit der Zerstörung und umfasst viele Zustände dazwischen. Wenn sich der Status einer Aktivität ändert, wird die entsprechende Lebenszyklusereignismethode aufgerufen, um die Aktivität über die bevorstehende Zustandsänderung zu informieren und ihr die Möglichkeit zu geben, Code auszuführen, um sich an diese Änderung anzupassen. In diesem Artikel wird der Lebenszyklus von Aktivitäten untersucht und die Verantwortung erläutert, die eine Aktivität während jeder dieser Zustandsänderungen hat, um Teil einer gut verhaltenen, zuverlässigen Anwendung zu sein.

Übersicht über den Aktivitätslebenszyklus

Aktivitäten sind ein ungewöhnliches Programmierkonzept speziell für Android. Bei der herkömmlichen Anwendungsentwicklung gibt es in der Regel eine statische Hauptmethode, die zum Starten der Anwendung ausgeführt wird. Bei Android sind die Dinge jedoch anders; Android-Anwendungen können über jede registrierte Aktivität innerhalb einer Anwendung gestartet werden. In der Praxis verfügen die meisten Anwendungen nur über eine bestimmte Aktivität, die als Einstiegspunkt der Anwendung angegeben ist. Wenn eine Anwendung jedoch abstürzt oder vom Betriebssystem beendet wird, kann das Betriebssystem versuchen, die Anwendung bei der letzten geöffneten Aktivität oder an einer anderen Stelle im vorherigen Aktivitätsstapel neu zu starten. Darüber hinaus kann das Betriebssystem Aktivitäten anhalten, wenn sie nicht aktiv sind, und sie zurückfordern, wenn der Arbeitsspeicher niedrig ist. Es muss sorgfältig geprüft werden, damit die Anwendung ihren Zustand ordnungsgemäß wiederherstellen kann, wenn eine Aktivität neu gestartet wird, insbesondere, wenn diese Aktivität von Daten aus früheren Aktivitäten abhängt.

Der Aktivitätslebenszyklus wird als Sammlung von Methoden implementiert, die vom Betriebssystem während des gesamten Lebenszyklus einer Aktivität aufgerufen werden. Mit diesen Methoden können Entwickler die Funktionalität implementieren, die erforderlich ist, um die Status- und Ressourcenverwaltungsanforderungen ihrer Anwendungen zu erfüllen.

Für den Entwickler einer Anwendung ist es äußerst wichtig, die Anforderungen jeder Aktivität zu analysieren, um festzustellen, welche Methoden, die der Aktivitätslebenszyklus zur Verfügung stellt, implementiert werden müssen. Das Nichteinhalten dieser Anforderung kann zu Instabilität der Anwendung, Abstürzen, übermäßigem Ressourcenverbrauch und möglicherweise sogar Instabilität des zugrunde liegenden Betriebssystems führen.

In diesem Kapitel wird der Aktivitätslebenszyklus ausführlich untersucht, einschließlich:

  • Aktivitätsstatus
  • Lebenszyklusmethoden
  • Beibehalten des Anwendungsstatus

Dieser Abschnitt enthält auch eine Schritt-für-Schritt-Anleitung, die praktische Beispiele zum effizienten Speichern des Zustands während des Aktivitätslebenszyklus bereitstellt. Am Ende dieses Kapitels sollten Sie über ein Verständnis des Aktivitätslebenszyklus und darüber verfügen, wie Sie ihn in einer Android-Anwendung unterstützen.

Aktivitätslebenszyklus

Der Android-Aktivitätslebenszyklus umfasst eine Sammlung von Methoden, die innerhalb der Activity-Klasse verfügbar gemacht werden, die dem Entwickler ein Ressourcenverwaltungsframework bereitstellen. Dieses Framework ermöglicht Es Entwicklern, die eindeutigen Statusverwaltungsanforderungen jeder Aktivität in einer Anwendung zu erfüllen und die Ressourcenverwaltung ordnungsgemäß zu verarbeiten.

Aktivitätsstatus

Das Betriebssystem Android vermittelt Aktivitäten basierend auf ihrem Status. Dies hilft Android, Aktivitäten zu identifizieren, die nicht mehr verwendet werden, sodass das Betriebssystem Arbeitsspeicher und Ressourcen zurückgewinnen kann. Das folgende Diagramm veranschaulicht die Zustände, die eine Aktivität während ihrer Lebensdauer durchlaufen kann:

Aktivitätsstatusdiagramm

Diese Zustände können wie folgt in vier Hauptgruppen unterteilt werden:

  1. Aktiv oder laufend – Aktivitäten werden als aktiv oder laufend betrachtet, wenn sie sich im Vordergrund befinden, auch als oberste Position im Aktivitätsstapel bezeichnet. Dies gilt als die Aktivität mit der höchsten Priorität in Android und wird daher nur in extremen Situationen vom Betriebssystem getötet, z. B. wenn die Aktivität versucht, mehr Arbeitsspeicher zu verwenden, als auf dem Gerät verfügbar ist, da dies dazu führen könnte, dass die Benutzeroberfläche nicht mehr reagiert.

  2. Angehalten – Wenn das Gerät in den Ruhezustand wechselt oder eine Aktivität weiterhin sichtbar ist, aber teilweise durch eine neue, nicht vollständige oder transparente Aktivität ausgeblendet wird, wird die Aktivität als angehalten betrachtet. Angehaltene Aktivitäten sind weiterhin aktiv, d. h., sie verwalten alle Status- und Mitgliedsinformationen und bleiben dem Fenster-Manager zugeordnet. Dies gilt als die zweithöchste Aktivität in Android und wird daher nur vom Betriebssystem getötet, wenn das Töten dieser Aktivität die Ressourcenanforderungen erfüllt, die erforderlich sind, um die Aktive/Laufende Aktivität stabil und reaktionsfähig zu halten.

  3. Beendet/hintergrundiert – Aktivitäten, die von einer anderen Aktivität vollständig verdeckt werden, werden als beendet oder im Hintergrund betrachtet. Beendete Aktivitäten versuchen weiterhin, ihre Status- und Mitgliedsinformationen so lange wie möglich aufzubewahren, aber beendete Aktivitäten werden als die niedrigste Priorität der drei Staaten angesehen, und das Betriebssystem beendet Aktivitäten in diesem Zustand zuerst, um die Ressourcenanforderungen höherer Prioritätsaktivitäten zu erfüllen.

  4. Neu gestartet – Es ist möglich, dass eine Aktivität, die sich im Lebenszyklus in einem Zustand von angehalten bis gestoppt befindet, von Android aus dem Speicher entfernt wird. Wenn der Benutzer zurück zu der Aktivität navigiert, muss sie neu gestartet werden, in den zuvor gespeicherten Zustand wiederhergestellt und dann dem Benutzer angezeigt werden.

Aktivität Re-Creation als Reaktion auf Konfigurationsänderungen

Um die Sache noch komplizierter zu machen, wirft Android noch einen weiteren Faktor in die Waagschale: Konfigurationsänderungen. Konfigurationsänderungen sind schnelle Aktivitätsvernichtungs-/Neuerstellungszyklen, die auftreten, wenn sich die Konfiguration einer Aktivität ändert, z. B. wenn das Gerät gedreht wird (und die Aktivität im Quer- oder Hochformatmodus neu integriert werden muss), wenn die Tastatur angezeigt wird (und die Aktivität die Möglichkeit bietet, die Größe selbst zu ändern), oder wenn das Gerät in einem Dock platziert wird, unter anderem.

Konfigurationsänderungen verursachen weiterhin dieselben Aktivitätsstatusänderungen, die beim Beenden und Neustarten einer Aktivität auftreten würden. Um jedoch sicherzustellen, dass eine Anwendung bei Konfigurationsänderungen gut reagiert und gut funktioniert, ist es wichtig, dass sie so schnell wie möglich behandelt werden. Aus diesem Fall verfügt Android über eine bestimmte API, die verwendet werden kann, um den Zustand während konfigurationsänderungen beizubehalten. Dies wird später im Abschnitt „Verwalten des Zustands während des Lebenszyklus“ behandelt.

Lebenszyklus-Methoden für Aktivitäten

Das Android SDK und das Xamarin.Android-Framework bieten ein leistungsfähiges Modell zum Verwalten des Status von Aktivitäten innerhalb einer Anwendung. Wenn sich der Status einer Aktivität ändert, wird die Aktivität vom Betriebssystem benachrichtigt, wodurch bestimmte Methoden für diese Aktivität aufgerufen werden. Das folgende Diagramm veranschaulicht diese Methoden im Verhältnis zum Aktivitätslebenszyklus:

Aktivitätslebenszyklus-Flussdiagramm

Als Entwickler können Sie Zustandsänderungen behandeln, indem Sie diese Methoden innerhalb einer Aktivität überschreiben. Beachten Sie jedoch, dass alle Lebenszyklusmethoden im UI-Thread aufgerufen werden und das Betriebssystem daran hindert, die nächste Ui-Arbeit auszuführen, z. B. das Ausblenden der aktuellen Aktivität, das Anzeigen einer neuen Aktivität usw. Daher sollten Code in diesen Methoden so kurz wie möglich sein, damit eine Anwendung das Gefühl hat, gut zu funktionieren. Alle länger laufenden Aufgaben sollten in einem Hintergrundthread ausgeführt werden.

Lassen Sie uns jede dieser Lebenszyklusmethoden und deren Verwendung untersuchen:

OnCreate

OnCreate ist die erste Methode, die aufgerufen werden soll, wenn eine Aktivität erstellt wird. OnCreate wird immer überschrieben, um alle Startinitialisierungen durchzuführen, die von einer Aktivität benötigt werden, wie z. B:

  • Erstellen von Ansichten
  • Initialisieren von Variablen
  • Binden statischer Daten an Listen

OnCreate verwendet einen Bundle-Parameter , bei dem es sich um ein Wörterbuch zum Speichern und Übergeben von Statusinformationen und Objekten zwischen Aktivitäten handelt, wenn das Bundle nicht null ist, gibt dies an, dass die Aktivität neu gestartet wird und der Status aus der vorherigen Instanz wiederhergestellt werden soll. Der folgende Code veranschaulicht, wie Werte aus dem Bundle abgerufen werden:

protected override void OnCreate(Bundle bundle)
{
   base.OnCreate(bundle);

   string intentString;
   bool intentBool;

   if (bundle != null)
   {
      intentString = bundle.GetString("myString");
      intentBool = bundle.GetBoolean("myBool");
   }

   // Set our view from the "main" layout resource
   SetContentView(Resource.Layout.Main);
}

Nachdem OnCreate abgeschlossen ist, ruft Android OnStart an.

OnStart

OnStart wird immer vom System aufgerufen, nachdem OnCreate der Vorgang abgeschlossen ist. Aktivitäten können diese Methode außer Kraft setzen, wenn sie bestimmte Aufgaben direkt ausführen müssen, bevor eine Aktivität sichtbar wird, z. B. das Aktualisieren der aktuellen Werte von Ansichten innerhalb der Aktivität. Android ruft unmittelbar nach dieser Methode auf OnResume .

OnResume

Das System ruft OnResume auf, wenn die Aktivität bereit ist, mit der Interaktion mit dem Benutzer zu beginnen. Aktivitäten sollten diese Methode außer Kraft setzen, um Aufgaben auszuführen, z. B.:

  • Hochfahren von Bildfrequenzen (eine häufige Aufgabe bei der Spieleentwicklung)
  • Starten von Animationen
  • Auf GPS-Updates warten
  • Anzeigen relevanter Warnungen oder Dialogfelder
  • Externe Event Handler einbinden

Der folgende Codeausschnitt zeigt beispielsweise, wie die Kamera initialisiert wird:

protected override void OnResume()
{
    base.OnResume(); // Always call the superclass first.

    if (_camera==null)
    {
        // Do camera initializations here
    }
}

OnResume ist wichtig, weil jeder Vorgang, der in OnPause ausgeführt wird, in OnResume rückgängig gemacht werden sollte, da es die einzige Lebenszyklusmethode ist, die garantiert nach OnPause ausgeführt wird, wenn die Aktivität wieder aktiviert wird.

OnPause

OnPause wird aufgerufen, wenn das System die Aktivität in den Hintergrund versetzen wird oder wenn die Aktivität teilweise verdeckt wird. Aktivitäten sollten diese Methode außer Kraft setzen, wenn dies erforderlich ist:

  • Ungespeicherte Änderungen an persistenten Daten committen

  • Zerstören oder Bereinigen anderer Objekte, die Ressourcen verbrauchen

  • Senken der Bildfrequenzen und Anhalten von Animationen

  • Heben Sie die Registrierung externer Ereignishandler oder Benachrichtigungshandler auf (d. h. solche, die an einen Dienst gebunden sind). Dies muss erfolgen, um Speicherverluste im Aktivitätsspeicher zu verhindern.

  • Ebenso müssen Dialogfelder und Warnungen mit der .Dismiss()-Methode bereinigt werden, wenn die Aktivität solche angezeigt hat.

Der folgende Codeausschnitt gibt beispielsweise die Kamera frei, da die Aktivität nicht verwendet werden kann, während sie angehalten wurde:

protected override void OnPause()
{
    base.OnPause(); // Always call the superclass first

    // Release the camera as other activities might need it
    if (_camera != null)
    {
        _camera.Release();
        _camera = null;
    }
}

Es gibt zwei mögliche Lifecycle-Methoden, die nach OnPause aufgerufen werden:

  1. OnResume wird aufgerufen, wenn die Aktivität an den Vordergrund zurückgegeben werden soll.
  2. OnStop wird aufgerufen, wenn die Aktivität im Hintergrund platziert wird.

OnStop

OnStop wird aufgerufen, wenn die Aktivität für den Benutzer nicht mehr sichtbar ist. Dies geschieht, wenn eines der folgenden eintritt:

  • Eine neue Aktivität wird gestartet und überdeckt diese Aktivität.
  • Eine vorhandene Aktivität wird in den Vordergrund gebracht.
  • Die Aktivität wird zerstört.

OnStop kann nicht immer in Situationen mit geringem Arbeitsspeicher aufgerufen werden, z. B. wenn Android auf Ressourcen ausgehungert ist und die Aktivität nicht ordnungsgemäß hintergrundieren kann. Aus diesem Grund sollte man sich am besten nicht darauf verlassen, dass OnStop aufgerufen wird, wenn eine Aktivität zur Zerstörung vorbereitet wird. Die nächsten Lebenszyklusmethoden, die nach diesem Aufruf aufgerufen werden können, sind OnDestroy , wenn die Aktivität abläuft oder OnRestart wenn die Aktivität zur Interaktion mit dem Benutzer zurückkehrt.

OnDestroy

OnDestroy ist die endgültige Methode, die für eine Activity-Instanz aufgerufen wird, bevor sie zerstört und vollständig aus dem Speicher entfernt wird. In extremen Situationen kann Android den Anwendungsprozess beenden, der die Aktivität hostet, was dazu führt, dass OnDestroy nicht aufgerufen wird. Die meisten Aktivitäten werden diese Methode nicht implementieren, da die meisten Aufräum- und Abschaltvorgänge bereits in den Methoden OnPause und OnStop durchgeführt wurden. Die OnDestroy-Methode wird in der Regel überschrieben, um lang ausgeführte Aufgaben zu bereinigen, die Ressourcen vergeuden könnten. Ein Beispiel hierfür könnten Hintergrundthreads sein, die in OnCreate gestartet wurden.

Es gibt keine Lebenszyklusmethoden, die aufgerufen werden, nachdem die Aktivität zerstört wurde.

OnRestart

OnRestart wird aufgerufen, nachdem Ihre Aktivität beendet wurde, bevor sie erneut gestartet wird. Ein gutes Beispiel hierfür wäre, wenn der Benutzer während einer Aktivität in der Anwendung die Startschaltfläche drückt. Wenn dies geschieht, werden die OnPause und OnStop Methoden aufgerufen, und die Aktivität wird in den Hintergrund verschoben, aber nicht zerstört. Wenn der Benutzer dann die Anwendung mithilfe des Task-Managers oder einer ähnlichen Anwendung wiederherstellen würde, ruft Android die OnRestart Methode der Aktivität auf.

Es gibt keine allgemeinen Richtlinien für die Art der Logik, die in OnRestart implementiert werden soll. Dies liegt daran, dass OnStart immer aufgerufen wird, unabhängig davon, ob die Aktivität erstellt oder neu gestartet wird, sodass alle ressourcen, die von der Aktivität benötigt werden, initialisiert OnStartwerden sollten, statt OnRestartin .

Die nächste Lebenszyklusmethode, die nach OnRestart aufgerufen wird, ist OnStart.

Zurück vs. Start

Viele Android-Geräte verfügen über zwei unterschiedliche Schaltflächen: eine Schaltfläche "Zurück" und eine Schaltfläche "Start". Ein Beispiel hierfür ist im folgenden Screenshot von Android 4.0.3 zu sehen:

Schaltflächen

Es gibt einen subtilen Unterschied zwischen den beiden Schaltflächen, obwohl sie scheinbar den gleichen Effekt haben, eine Anwendung im Hintergrund zu platzieren. Wenn ein Benutzer auf die Schaltfläche "Zurück" klickt, teilt er Android mit, dass er mit der Aktivität fertig ist. Android zerstört die Aktivität. Im Gegensatz dazu, wenn der Benutzer auf die Schaltfläche "Start" klickt, wird die Aktivität lediglich im Hintergrund platziert – Android beendet die Aktivität nicht.

Zustandsverwaltung über den gesamten Lebenszyklus hinweg

Wenn eine Aktivität beendet oder zerstört wird, bietet das System die Möglichkeit, den Status der Aktivität für eine spätere Rehydratation zu speichern. Dieser gespeicherte Zustand wird als Instanzstatus bezeichnet. Android bietet drei Optionen zum Speichern des Instanzstatus während des Aktivitätslebenszyklus:

  1. Speichern von Grundtypwerten in einem Dictionary sogenannten Bundle , das Android zum Speichern des Zustands verwendet.

  2. Erstellen einer benutzerdefinierten Klasse, die komplexe Werte wie Bitmaps enthält. Android verwendet diese benutzerdefinierte Klasse zum Speichern des Zustands.

  3. Umgehung des Lebenszyklus von Konfigurationsänderungen und Übernahme der vollständigen Verantwortung für die Aufrechterhaltung des Status in der Aktivität.

In diesem Leitfaden werden die ersten beiden Optionen behandelt.

Bundle Status

Die primäre Option zum Speichern des Instanzstatus ist die Verwendung eines Schlüssel-Wert-Wörterbuchobjekts, das als Bundle bezeichnet wird. Denken Sie daran, dass beim Erstellen einer Aktivität die Methode OnCreate ein Bundle als Parameter übergeben bekommt und dieses Bundle zum Wiederherstellen des Instanzstatus verwendet werden kann. Es wird nicht empfohlen, ein Bündel für komplexere Daten zu verwenden, die nicht schnell oder einfach in Schlüssel-Wert-Paare serialisiert werden (z. B. Bitmaps); Stattdessen sollte sie für einfache Werte wie Zeichenfolgen verwendet werden.

Eine Aktivität bietet Methoden zum Speichern und Abrufen des Instanzstatus im Bundle:

  • OnSaveInstanceState – Dies wird von Android aufgerufen, wenn die Aktivität zerstört wird. Aktivitäten können diese Methode implementieren, wenn alle Schlüssel-/Wertstatuselemente beibehalten werden müssen.

  • OnRestoreInstanceState – Dies wird aufgerufen, nachdem die Methode abgeschlossen wurde, und bietet eine weitere Möglichkeit für eine Aktivität, ihren Zustand nach Abschluss der OnCreate Initialisierung wiederherzustellen.

Das folgende Diagramm veranschaulicht, wie diese Methoden verwendet werden:

Bündelstatusflussdiagramm

OnSaveInstanceState

OnSaveInstanceState wird aufgerufen, wenn die Aktivität beendet wird. Er empfängt einen Bündelparameter, in dem die Aktivität ihren Status speichern kann. Wenn ein Gerät eine Konfigurationsänderung erfährt, kann eine Activity das übergebene Bundle-Objekt verwenden, um den Status der Activity beizubehalten, indem OnSaveInstanceState überschrieben wird. Betrachten Sie z. B. den folgenden Code:

int c;

protected override void OnCreate (Bundle bundle)
{
  base.OnCreate (bundle);

  this.SetContentView (Resource.Layout.SimpleStateView);

  var output = this.FindViewById<TextView> (Resource.Id.outputText);

  if (bundle != null) {
    c = bundle.GetInt ("counter", -1);
  } else {
    c = -1;
  }

  output.Text = c.ToString ();

  var incrementCounter = this.FindViewById<Button> (Resource.Id.incrementCounter);

  incrementCounter.Click += (s,e) => {
    output.Text = (++c).ToString();
  };
}

Der obige Code erhöht eine ganze Zahl mit dem Namen c, wenn auf eine Schaltfläche mit dem Namen incrementCounter geklickt wird, und zeigt das Ergebnis in einem TextView mit dem Namen output an. Wenn eine Konfigurationsänderung erfolgt – z. B. wenn das Gerät gedreht wird – würde der obige Code den Wert von c verlieren, weil bundle zu null wird, wie in der folgenden Abbildung dargestellt.

Anzeige zeigt keinen vorherigen Wert an.

Um den Wert c in diesem Beispiel beizubehalten, kann die Aktivität außer Kraft setzen OnSaveInstanceStateund den Wert im Bündel speichern, wie unten dargestellt:

protected override void OnSaveInstanceState (Bundle outState)
{
  outState.PutInt ("counter", c);
  base.OnSaveInstanceState (outState);
}

Wenn das Gerät nun in eine neue Ausrichtung gedreht wird, wird die ganze Zahl im Bundle gespeichert und mit der Zeile abgerufen:

c = bundle.GetInt ("counter", -1);

Hinweis

Es ist wichtig, immer die Basisimplementierung OnSaveInstanceState aufzurufen, damit auch der Zustand der Ansichtshierarchie gespeichert werden kann.

Status anzeigen

Das Überschreiben von OnSaveInstanceState ist ein geeigneter Mechanismus zum Speichern von temporären Daten in einer Activity bei Änderungen der Ausrichtung, wie der Zähler im obigen Beispiel. Die Standardimplementierung OnSaveInstanceState übernimmt jedoch das Speichern vorübergehender Daten in der Benutzeroberfläche für jede Ansicht, solange jeder Ansicht eine ID zugewiesen ist. Angenommen, eine Anwendung hat ein EditText Element in XML wie folgt definiert:

<EditText android:id="@+id/myText"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"/>

Da das EditText-Steuerelement ein id enthält, bleiben die Daten sichtbar, wenn der Benutzer Daten eingibt und das Gerät dreht, wie unten dargestellt.

Daten werden im Querformat beibehalten.

OnRestoreInstanceState

OnRestoreInstanceState wird nach OnStart aufgerufen. Es bietet einer Aktivität die Möglichkeit, jeden Status wiederherzustellen, der zuvor in einem Bundle während des vorherigen OnSaveInstanceState gespeichert wurde. Dabei handelt es sich jedoch um dasselbe Bundle, das auch OnCreate zur Verfügung gestellt wird.

Der folgende Code veranschaulicht, wie der Zustand wiederhergestellt werden kann:OnRestoreInstanceState

protected override void OnRestoreInstanceState(Bundle savedState)
{
    base.OnRestoreInstanceState(savedState);
    var myString = savedState.GetString("myString");
    var myBool = savedState.GetBoolean("myBool");
}

Diese Methode ist vorhanden, um einige Flexibilität bei der Wiederherstellung des Zustands bereitzustellen. Manchmal ist es besser, zu warten, bis alle Initialisierungen durchgeführt werden, bevor der Instanzzustand wiederhergestellt wird. Darüber hinaus kann eine Unterklasse einer vorhandenen Aktivität nur bestimmte Werte aus dem Instanzstatus wiederherstellen. In vielen Fällen ist es nicht notwendig, OnRestoreInstanceState zu überschreiben, da die meisten Aktivitäten den Status mit dem für OnCreate bereitgestellten Bundle wiederherstellen können.

Ein Beispiel für das Speichern des Zustands mit einem Bundle, siehe die Schritt-für-Schritt-Anleitung - Speichern des Aktivitätszustands.

Bündelbeschränkungen

Obwohl OnSaveInstanceState es leicht ist, vorübergehende Daten zu speichern, gelten einige Einschränkungen:

  • Es wird nicht in allen Fällen aufgerufen. Wenn Sie z. B. Home oder Zurück drücken, um eine Aktivität zu beenden, wird dies nicht dazu führen, dass OnSaveInstanceState aufgerufen wird.

  • Das übergebene OnSaveInstanceState Paket ist nicht für große Objekte wie Bilder konzipiert. Bei großen Objekten ist das Speichern des Objekts aus OnRetainNonConfigurationInstance vorzuziehen, wie unten beschrieben.

  • Daten, die mithilfe des Bundles gespeichert werden, werden serialisiert, was zu Verzögerungen führen kann.

Der Bündelstatus ist nützlich für einfache Daten, die nicht viel Arbeitsspeicher verwenden, während Nichtkonfigurationsinstanzdaten für komplexere Daten oder daten, die teuer abzurufen sind, z. B. aus einem Webdienstaufruf oder einer komplizierten Datenbankabfrage, nützlich sind. Bei Bedarf werden Nichtkonfigurationsinstanzdaten in einem Objekt gespeichert. Im nächsten Abschnitt wird OnRetainNonConfigurationInstance als eine Möglichkeit vorgestellt, komplexere Datentypen durch Konfigurationsänderungen zu erhalten.

Persistieren komplexer Daten

Zusätzlich zur Persistenz von Daten im Bundle unterstützt Android auch das Speichern von Daten, indem OnRetainNonConfigurationInstance überschrieben wird und eine Instanz eines Java.Lang.Object zurückgegeben wird, die die zu persistierenden Daten enthält. Es gibt zwei Hauptvorteile bei der Verwendung von OnRetainNonConfigurationInstance zum Speichern des Zustands:

  • Das zurückgegebene OnRetainNonConfigurationInstance Objekt funktioniert gut mit größeren, komplexeren Datentypen, da der Speicher dieses Objekt beibehält.

  • Die OnRetainNonConfigurationInstance Methode wird bei Bedarf und nur bei Bedarf aufgerufen. Dies ist kostengünstiger als die Verwendung eines manuellen Caches.

Die Verwendung OnRetainNonConfigurationInstance eignet sich für Szenarien, in denen es teuer ist, die Daten mehrmals abzurufen, z. B. in Webdienstaufrufen. Betrachten Sie beispielsweise den folgenden Code, der Twitter durchsucht:

public class NonConfigInstanceActivity : ListActivity
{
  protected override void OnCreate (Bundle bundle)
  {
    base.OnCreate (bundle);
    SearchTwitter ("xamarin");
  }

  public void SearchTwitter (string text)
  {
    string searchUrl = String.Format("http://search.twitter.com/search.json?" + "q={0}&rpp=10&include_entities=false&" + "result_type=mixed", text);

    var httpReq = (HttpWebRequest)HttpWebRequest.Create (new Uri (searchUrl));
    httpReq.BeginGetResponse (new AsyncCallback (ResponseCallback), httpReq);
  }

  void ResponseCallback (IAsyncResult ar)
  {
    var httpReq = (HttpWebRequest)ar.AsyncState;

    using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse (ar)) {
      ParseResults (httpRes);
    }
  }

  void ParseResults (HttpWebResponse httpRes)
  {
    var s = httpRes.GetResponseStream ();
    var j = (JsonObject)JsonObject.Load (s);

    var results = (from result in (JsonArray)j ["results"] let jResult = result as JsonObject select jResult ["text"].ToString ()).ToArray ();

    RunOnUiThread (() => {
      PopulateTweetList (results);
    });
  }

  void PopulateTweetList (string[] results)
  {
    ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
  }
}

Dieser Code ruft Ergebnisse aus dem Web ab, das als JSON formatiert ist, analysiert sie und zeigt dann die Ergebnisse in einer Liste an, wie im folgenden Screenshot gezeigt:

Auf dem Bildschirm angezeigte Ergebnisse

Wenn eine Konfigurationsänderung auftritt – z. B. wenn ein Gerät gedreht wird – wiederholt der Code den Prozess. Um die Ergebnisse, die ursprünglich abgerufen wurden, wiederzuverwenden und unnötige, redundante Netzwerkaufrufe zu vermeiden, können wir OnRetainNonconfigurationInstance nutzen, um die Ergebnisse zu speichern, wie unten dargestellt.

public class NonConfigInstanceActivity : ListActivity
{
  TweetListWrapper _savedInstance;

  protected override void OnCreate (Bundle bundle)
  {
    base.OnCreate (bundle);

    var tweetsWrapper = LastNonConfigurationInstance as TweetListWrapper;

    if (tweetsWrapper != null) {
      PopulateTweetList (tweetsWrapper.Tweets);
    } else {
      SearchTwitter ("xamarin");
    }

    public override Java.Lang.Object OnRetainNonConfigurationInstance ()
    {
      base.OnRetainNonConfigurationInstance ();
      return _savedInstance;
    }

    ...

    void PopulateTweetList (string[] results)
    {
      ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
      _savedInstance = new TweetListWrapper{Tweets=results};
    }
}

Wenn das Gerät gedreht wird, werden die ursprünglichen Ergebnisse aus der LastNonConfiguartionInstance Eigenschaft abgerufen. In diesem Beispiel bestehen die Ergebnisse aus einem string[], das Tweets enthält. Da OnRetainNonConfigurationInstance eine Rückgabe von Java.Lang.Object erfordert, wird die Klasse string[] in eine Klasse eingebettet, die eine Unterklasse von Java.Lang.Object ist, wie unten dargestellt:

class TweetListWrapper : Java.Lang.Object
{
  public string[] Tweets { get; set; }
}

Wenn Sie beispielsweise versuchen, ein TextView-Objekt als Rückgabewert von OnRetainNonConfigurationInstance zu verwenden, wird die Activity nicht funktionieren, wie der folgende Code zeigt:

TextView _textView;

protected override void OnCreate (Bundle bundle)
{
  base.OnCreate (bundle);

  var tv = LastNonConfigurationInstance as TextViewWrapper;

  if(tv != null) {
    _textView = tv;
    var parent = _textView.Parent as FrameLayout;
    parent.RemoveView(_textView);
  } else {
    _textView = new TextView (this);
    _textView.Text = "This will leak.";
  }

  SetContentView (_textView);
}

public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
  base.OnRetainNonConfigurationInstance ();
  return _textView;
}

In diesem Abschnitt haben wir erfahren, wie man einfache Zustandsdaten mit Bundle beibehalten und komplexere Datentypen mit OnRetainNonConfigurationInstance sichern kann.

Zusammenfassung

Der Android-Aktivitätslebenszyklus bietet ein leistungsfähiges Framework für die Zustandsverwaltung von Aktivitäten innerhalb einer Anwendung, kann aber schwierig sein, um sie zu verstehen und zu implementieren. In diesem Kapitel wurden die verschiedenen Zustände eingeführt, die eine Aktivität während ihrer Lebensdauer durchlaufen kann, sowie die Lebenszyklusmethoden, die diesen Zuständen zugeordnet sind. Als Nächstes wurden Anleitungen dazu bereitgestellt, welche Art von Logik in jeder dieser Methoden ausgeführt werden soll.