Aktivitätslebenszyklus

Aktivitäten sind ein grundlegender Baustein von Android-Anwendungen und können in einer Reihe verschiedener Zustände vorhanden sein. Der Aktivitätslebenszyklus beginnt mit der Instanziierung und endet mit der Zerstörung und umfasst viele Zustände dazwischen. Wenn eine Aktivität den Zustand ändert, wird die entsprechende Lebenszyklusereignismethode aufgerufen, die die Aktivität über die bevorstehende Zustandsänderung benachrichtigt und ihr ermöglicht, 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 ordnungsgemäßen, zuverlässigen Anwendung zu sein.

Übersicht über den Aktivitätslebenszyklus

Aktivitäten sind ein ungewöhnliches Programmierkonzept speziell für Android. In der herkömmlichen Anwendungsentwicklung gibt es in der Regel eine statische Standard-Methode, 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 Anwendungseinstiegspunkt angegeben wird. 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 innerhalb des vorherigen Aktivitätsstapels neu zu starten. Darüber hinaus kann das Betriebssystem Aktivitäten anhalten, wenn sie nicht aktiv sind, und sie zurückgibt, wenn es nicht genügend Arbeitsspeicher hat. Es muss sorgfältig überlegt werden, damit die Anwendung ihren Zustand ordnungsgemäß wiederherstellen kann, wenn eine Aktivität neu gestartet wird, insbesondere wenn diese Aktivität von Daten aus vorherigen Aktivitäten abhängt.

Der Aktivitätslebenszyklus wird als Sammlung von Methoden implementiert, die das Betriebssystem während des gesamten Lebenszyklus einer Aktivität aufruft. Mit diesen Methoden können Entwickler die Funktionalität implementieren, die erforderlich ist, um die Anforderungen an die Zustands- und Ressourcenverwaltung ihrer Anwendungen zu erfüllen.

Es ist äußerst wichtig, dass der Anwendungsentwickler die Anforderungen jeder Aktivität analysiert, um zu bestimmen, welche Methoden, die durch den Aktivitätslebenszyklus verfügbar gemacht werden, implementiert werden müssen. Andernfalls kann dies zu Anwendungsinstabilität, Abstürze, Ressourcenblähungen und möglicherweise sogar zu zugrunde liegender Betriebssysteminstabilität führen.

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

  • Aktivitätsstatus
  • Lebenszyklusmethoden
  • Beibehalten des Status einer Anwendung

Dieser Abschnitt enthält auch eine exemplarische Vorgehensweise , die praktische Beispiele zum effizienten Speichern des Zustands während des Aktivitätslebenszyklus enthält. Am Ende dieses Kapitels sollten Sie den Aktivitätslebenszyklus und dessen Unterstützung in einer Android-Anwendung verstehen.

Aktivitätslebenszyklus

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

Aktivitätsstatus

Das Android-Betriebssystem vermittelt Aktivitäten basierend auf ihrem Zustand. Dies hilft Android dabei, 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 Standard Gruppen unterteilt werden:

  1. Aktiv oder Ausgeführt : Aktivitäten werden als aktiv oder ausgeführt betrachtet, wenn sie sich im Vordergrund befinden, auch als oberster Bereich des Aktivitätsstapels bezeichnet. Dies gilt als Aktivität mit der höchsten Priorität in Android und wird daher nur in Extremsituationen vom Betriebssystem beendet, 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 Standbymodus wechselt oder eine Aktivität noch sichtbar, aber teilweise durch eine neue, nicht vollständige oder transparente Aktivität ausgeblendet ist, wird die Aktivität als angehalten betrachtet. Angehaltene Aktivitäten sind noch aktiv, d. h., sie behalten alle Status- und Mitgliedsinformationen bei und bleiben an den Fenster-Manager angefügt. Dies gilt als aktivität mit der zweithöchsten Priorität in Android und wird daher vom Betriebssystem nur beendet, wenn das Beenden dieser Aktivität die Ressourcenanforderungen erfüllt, die erforderlich sind, um die Aktive/Laufende Aktivität stabil und reaktionsfähig zu halten.

  3. Beendet/Hintergrund: 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 beizubehalten, aber beendete Aktivitäten werden als die niedrigste Priorität der drei Zustände angesehen, und daher beendet das Betriebssystem zuerst Aktivitäten in diesem Zustand, um die Ressourcenanforderungen von Aktivitäten mit höherer Priorität zu erfüllen.

  4. Neustarten : Es ist möglich, dass eine Aktivität, die im Lebenszyklus angehalten bis angehalten ist, von Android aus dem Arbeitsspeicher entfernt wird. Wenn der Benutzer zurück zur Aktivität navigiert, muss sie neu gestartet, in den zuvor gespeicherten Zustand wiederhergestellt und dann für den Benutzer angezeigt werden.

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

Um die Sache komplizierter zu machen, löst Android einen weiteren Schraubenschlüssel in der Mischung namens Konfigurationsänderungen aus. Konfigurationsänderungen sind schnelle Aktivitätszerstörungs-/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 erstellt werden muss), wenn die Tastatur angezeigt wird (und die Aktivität die Möglichkeit hat, sich selbst zu ändern) oder wenn das Gerät in einem Dock platziert wird. unter anderem.

Konfigurationsänderungen verursachen weiterhin die gleichen Aktivitätsstatusänderungen, die beim Beenden und Neustarten einer Aktivität auftreten würden. Um jedoch sicherzustellen, dass sich eine Anwendung reaktionsfähig fühlt und bei Konfigurationsänderungen gut funktioniert, ist es wichtig, dass sie so schnell wie möglich verarbeitet werden. Daher verfügt Android über eine bestimmte API, die verwendet werden kann, um den Zustand bei Konfigurationsänderungen beizubehalten. Dies wird später im Abschnitt Verwalten des Zustands im gesamten Lebenszyklus behandelt.

Aktivitätslebenszyklus-Methoden

Das Android SDK und das Xamarin.Android-Framework bieten ein leistungsfähiges Modell zum Verwalten des Status von Aktivitäten in einer Anwendung. Wenn sich der Status einer Aktivität ändert, wird die Aktivität vom Betriebssystem benachrichtigt, das bestimmte Methoden für diese Aktivität aufruft. Das folgende Diagramm veranschaulicht diese Methoden in Bezug auf den Aktivitätslebenszyklus:

Aktivitätslebenszyklus-Flussdiagramm

Als Entwickler können Sie Zustandsänderungen verarbeiten, indem Sie diese Methoden innerhalb einer Aktivität überschreiben. Es ist jedoch wichtig zu beachten, dass alle Lebenszyklusmethoden im UI-Thread aufgerufen werden und das Betriebssystem daran hindert, die nächste Arbeit der Benutzeroberfläche auszuführen, z. B. das Ausblenden der aktuellen Aktivität, das Anzeigen einer neuen Aktivität usw. Daher sollte der Code in diesen Methoden so kurz wie möglich sein, damit sich eine Anwendung gut funktioniert. Alle Aufgaben mit langer Ausführungsdauer sollten in einem Hintergrundthread ausgeführt werden.

Sehen wir uns die einzelnen Lebenszyklusmethoden und deren Verwendung an:

OnCreate

OnCreate ist die erste Methode, die aufgerufen wird, wenn eine Aktivität erstellt wird. OnCreate wird immer überschrieben, um alle Startinitialisierungen auszuführen, die für eine Aktivität erforderlich sein können, z. B.:

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

OnCreatenimmt einen Bundle-Parameter an. Dabei handelt es sich um ein Wörterbuch zum Speichern und Übergeben von Zustandsinformationen und Objekten zwischen Aktivitäten. Wenn das Bundle nicht NULL ist, gibt dies an, dass die Aktivität neu gestartet wird und ihr Zustand aus dem vorherigen instance 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);
}

Sobald OnCreate der Vorgang abgeschlossen ist, ruft Android auf OnStart.

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 ausführen müssen, bevor eine Aktivität sichtbar wird, z. B. das Aktualisieren aktueller 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 überschreiben, um Aufgaben wie die folgenden auszuführen:

  • Hochfahren von Bildfrequenzen (eine häufige Aufgabe bei der Spieleentwicklung)
  • Starten von Animationen
  • Lauschen auf GPS-Updates
  • Anzeigen relevanter Warnungen oder Dialogfelder
  • Verknüpfen externer Ereignishandler

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, da jeder Vorgang, der in OnPause ausgeführt wird, in OnResumenicht ausgeführt werden sollte, da es sich um die einzige Lebenszyklusmethode handelt, die garantiert nach OnPause ausgeführt wird, wenn die Aktivität wieder zum Leben erweckt wird.

Onpause

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

  • Committen von nicht gespeicherten Änderungen an persistenten Daten

  • Zerstören oder sauber andere Objekte, die Ressourcen verbrauchen

  • Herunterfahren von 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 geschehen, um Speicherverluste der Aktivität zu verhindern.

  • Wenn die Aktivität Dialogfelder oder Warnungen angezeigt hat, müssen diese mit der .Dismiss() -Methode bereinigt werden.

Der folgende Codeausschnitt gibt beispielsweise die Kamera frei, da die Aktivität sie während des Anhaltens nicht nutzen kann:

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 Lebenszyklusmethoden, die nach OnPauseaufgerufen werden:

  1. OnResume wird aufgerufen, wenn die Aktivität in 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 einer der folgenden Auftritte auftritt:

  • Eine neue Aktivität wird gestartet und verschleiert diese Aktivität.
  • Eine vorhandene Aktivität wird in den Vordergrund gestellt.
  • Die Aktivität wird zerstört.

OnStop kann in Situationen mit wenig Arbeitsspeicher nicht immer aufgerufen werden, z. B. wenn Android nach Ressourcen ausgehungert ist und die Aktivität nicht ordnungsgemäß hintergrunden kann. Aus diesem Grund ist es am besten, sich beim Vorbereiten einer Aktivität auf die Zerstörung nicht darauf OnStop zu verlassen, aufgerufen zu werden. Die nächsten Lebenszyklusmethoden, die nach dieser aufgerufen werden können, sind OnDestroy , wenn die Aktivität abläuft oder OnRestart wenn die Aktivität zurückkommt, um mit dem Benutzer zu interagieren.

OnDestroy

OnDestroy ist die letzte Methode, die für eine Aktivität aufgerufen wird, instance bevor sie zerstört und vollständig aus dem Arbeitsspeicher entfernt wird. In extremen Situationen kann Android den Anwendungsprozess beenden, der die Aktivität hostt, was dazu führt OnDestroy , dass nicht aufgerufen wird. Die meisten Aktivitäten implementieren diese Methode nicht, da die meisten sauber In- und Herunterfahren in den OnPause Methoden und OnStop durchgeführt wurden. Die OnDestroy Methode wird in der Regel überschrieben, um aufgaben mit langer Ausführungsdauer zu sauber, die ressourcenlecken können. Ein Beispiel hierfür könnten Hintergrundthreads sein, die in OnCreategestartet wurden.

Es werden keine Lebenszyklusmethoden aufgerufen, 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 die Startschaltfläche während einer Aktivität in der Anwendung drückt. Wenn dies geschieht OnPause , OnStop werden 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 sollte, ruft Android die OnRestart Methode der Aktivität auf.

Es gibt keine allgemeinen Richtlinien für die Art von Logik, die in OnRestartimplementiert werden sollte. Dies liegt daran, dass OnStart immer aufgerufen wird, unabhängig davon, ob die Aktivität erstellt oder neu gestartet wird. Daher sollten alle ressourcen, die für die Aktivität erforderlich sind, in OnStartOnRestartinitialisiert werden.

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

Back vs. Home

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

Schaltflächen

Es gibt einen subtilen Unterschied zwischen den beiden Schaltflächen, auch wenn sie 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. Wenn der Benutzer hingegen auf die Schaltfläche Start klickt, wird die Aktivität nur im Hintergrund platziert – Android beendet die Aktivität nicht.

Verwalten des Zustands während des gesamten Lebenszyklus

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 Rehydrierung zu speichern. Dieser gespeicherte Zustand wird als instance Zustand bezeichnet. Android bietet drei Optionen zum Speichern instance Zustands während des Aktivitätslebenszyklus:

  1. Speichern von primitiven Werten 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 Konfigurationsänderungslebenszyklus und Übernahme der vollständigen Verantwortung für die Aufrechterhaltung des Zustands in der Aktivität.

In diesem Leitfaden werden die ersten beiden Optionen behandelt.

Bündelstatus

Die primäre Option zum Speichern instance Zustands besteht darin, ein Schlüssel-Wert-Wörterbuchobjekt zu verwenden, das als Bundle bezeichnet wird. Wenn eine Aktivität erstellt wird, dass die OnCreate Methode ein Bundle als Parameter übergeben wird, kann dieses Bündel verwendet werden, um den instance Zustand wiederherzustellen. 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). sie sollte vielmehr für einfache Werte wie Zeichenfolgen verwendet werden.

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

  • OnSaveInstanceState : Dies wird von Android aufgerufen, wenn die Aktivität zerstört wird. Aktivitäten können diese Methode implementieren, wenn sie Schlüssel-Wert-Zustandselemente beibehalten müssen.

  • OnRestoreInstanceState : Dies wird aufgerufen, nachdem die OnCreate -Methode abgeschlossen ist, und bietet eine weitere Möglichkeit für eine Aktivität, ihren Zustand wiederherzustellen, nachdem die Initialisierung abgeschlossen ist.

Das folgende Diagramm veranschaulicht, wie diese Methoden verwendet werden:

Flussdiagramm für Bündelzustände

OnSaveInstanceState

OnSaveInstanceState wird aufgerufen, wenn die Aktivität beendet wird. Sie erhält einen Bündelparameter, in dem die Aktivität ihren Status speichern kann. Wenn ein Gerät eine Konfigurationsänderung erlebt, kann eine Aktivität das Bundle übergebene Objekt verwenden, um den Aktivitätsstatus beizubehalten, indem sie überschreibt OnSaveInstanceState. Beachten Sie z. B. 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 namens outputan. Wenn eine Konfigurationsänderung erfolgt - z. B. wenn das Gerät gedreht wird -, verliert der obige Code den Wert von c , da der bundle sein würde null, wie in der folgenden Abbildung dargestellt:

Anzeige zeigt den vorherigen Wert nicht an.

Um den Wert von c in diesem Beispiel beizubehalten, kann die Aktivität den Wert im Bundle wie unten gezeigt überschreiben OnSaveInstanceState:

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 von OnSaveInstanceState aufzurufen, damit auch der Zustand der Ansichtshierarchie gespeichert werden kann.

Ansichtsstatus

Das Überschreiben OnSaveInstanceState ist ein geeigneter Mechanismus zum Speichern vorübergehender Daten in einer Aktivität über Ausrichtungsänderungen hinweg, wie z. B. der Zähler im obigen Beispiel. Die Standardimplementierung von OnSaveInstanceState kümmert sich jedoch um das Speichern vorübergehender Daten in der Benutzeroberfläche für jede Ansicht, solange jeder Ansicht eine ID zugewiesen ist. Angenommen, eine Anwendung verfügt über ein EditText Element, das in XML wie folgt definiert ist:

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

Da dem EditText Steuerelement eine id zugewiesen ist, werden die Daten weiterhin angezeigt, wenn der Benutzer einige Daten eingibt und das Gerät rotiert, wie unten gezeigt:

Daten werden im Querformat beibehalten

OnRestoreInstanceState

OnRestoreInstanceState wird nach OnStartaufgerufen. Es bietet einer Aktivität die Möglichkeit, jeden Zustand wiederherzustellen, der zuvor während des vorherigen OnSaveInstanceStategespeichert wurde. Dies ist jedoch das gleiche Bündel, das für OnCreatebereitgestellt wird.

Der folgende Code veranschaulicht, wie der Zustand in OnRestoreInstanceStatewiederhergestellt werden kann:

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

Diese Methode bietet eine gewisse Flexibilität bei der Wiederherstellung des Zustands. Manchmal ist es sinnvoller, zu warten, bis alle Initialisierungen abgeschlossen sind, bevor sie instance Zustand wiederherstellen. Darüber hinaus kann eine Unterklasse einer vorhandenen Aktivität nur bestimmte Werte aus dem instance Zustand wiederherstellen. In vielen Fällen ist es nicht erforderlich, den Zustand zu überschreiben OnRestoreInstanceState, da die meisten Aktivitäten den Zustand mithilfe des für OnCreatebereitgestellten Bundles wiederherstellen können.

Ein Beispiel für das Speichern des Zustands mithilfe eines Bundlefinden Sie unter Exemplarische Vorgehensweise : Speichern des Aktivitätszustands.

Bundleeinschränkungen

Obwohl OnSaveInstanceState das Speichern vorübergehender Daten einfach ist, gibt es einige Einschränkungen:

  • Es wird nicht in allen Fällen aufgerufen. Wenn Sie z. B. start oder zurück drücken, um eine Aktivität zu beenden, wird OnSaveInstanceState nicht aufgerufen.

  • Das übergebene OnSaveInstanceState Bundle ist nicht für große Objekte, z. B. Bilder, konzipiert. Bei großen Objekten ist das Speichern des Objekts aus OnRetainNonConfigurationInstance vorzuziehen, wie unten erläutert.

  • Mithilfe des Bundles gespeicherte Daten werden serialisiert, was zu Verzögerungen führen kann.

Der Bündelzustand ist nützlich für einfache Daten, die nicht viel Arbeitsspeicher beanspruchen, während nicht konfigurierte instance Daten für komplexere Daten oder für Daten, die teuer abgerufen werden können, z. B. aus einem Webdienstaufruf oder einer komplizierten Datenbankabfrage, nützlich sind. Nicht konfigurierte instance Daten werden nach Bedarf in einem Objekt gespeichert. Im nächsten Abschnitt wird vorgestellt, wie komplexere Datentypen OnRetainNonConfigurationInstance durch Konfigurationsänderungen beibehalten werden können.

Beibehalten komplexer Daten

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

  • Das von OnRetainNonConfigurationInstance zurückgegebene Objekt funktioniert gut mit größeren, komplexeren Datentypen, da dieses Objekt im Arbeitsspeicher beibehalten wird.

  • 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 als JSON formatierten Web ab, analysiert sie und zeigt die Ergebnisse dann 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 ursprünglich abgerufenen Ergebnisse wiederzuverwenden und keine unnötigen, redundanten Netzwerkaufrufe zu verursachen, können OnRetainNonconfigurationInstance wir die Ergebnisse wie folgt speichern:

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 nun 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 erfordert, dass ein Java.Lang.Object zurückgegeben werden muss, wird in string[] eine Klasse umschlossen, die unterklassigt Java.Lang.Object, wie unten gezeigt:

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

Wenn Sie z. B. versuchen, ein TextView als Objekt zu verwenden, das von OnRetainNonConfigurationInstance zurückgegeben wird, wird die Aktivität verloren, wie im folgenden Code veranschaulicht:

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 gelernt, wie Einfache Zustandsdaten mit Bundlebeibehalten und komplexere Datentypen mit beibehalten werden OnRetainNonConfigurationInstance.

Zusammenfassung

Der Lebenszyklus von Android-Aktivitäten bietet ein leistungsstarkes Framework für die Zustandsverwaltung von Aktivitäten innerhalb einer Anwendung, aber es kann schwierig sein, sie zu verstehen und zu implementieren. In diesem Kapitel wurden die verschiedenen Zustände vorgestellt, die eine Aktivität während ihrer Lebensdauer durchlaufen kann, sowie die Lebenszyklusmethoden, die diesen Zuständen zugeordnet sind. Als Nächstes wurde eine Anleitung dazu bereitgestellt, welche Art von Logik in den einzelnen Methoden ausgeführt werden sollte.