2866 Stimmen

Wie kann ich einen Aktivitätsstatus mit der Funktion "Instanzstatus speichern" speichern?

Ich habe mit der Android-SDK-Plattform gearbeitet, und es ist ein wenig unklar, wie man den Status einer Anwendung speichert. Angesichts dieser geringfügigen Umrüstung des Beispiels "Hello, Android":

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

Ich dachte, es würde für den einfachsten Fall ausreichen, aber es antwortet immer mit der ersten Nachricht, egal wie ich von der App weg navigiere.

Ich bin sicher, dass die Lösung so einfach ist wie das Aufheben von onPause oder so ähnlich, aber ich habe etwa 30 Minuten lang in der Dokumentation gestöbert und nichts Eindeutiges gefunden.

2723voto

Reto Meier Punkte 95825

Sie müssen Folgendes überschreiben onSaveInstanceState(Bundle savedInstanceState) und schreiben Sie die Anwendungsstatuswerte, die Sie ändern wollen, in die Bundle Parameter wie diesen:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

Das Bundle ist im Wesentlichen eine Möglichkeit, eine NVP ("Name-Value Pair")-Map zu speichern, und es wird an onCreate() und auch onRestoreInstanceState() wo Sie dann die Werte aus der Aktivität wie folgt extrahieren würden:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

Oder aus einem Fragment.

@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);
    // Restore UI state from the savedInstanceState.
    // This bundle has also been passed to onCreate.
    boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
    double myDouble = savedInstanceState.getDouble("myDouble");
    int myInt = savedInstanceState.getInt("MyInt");
    String myString = savedInstanceState.getString("MyString");
}

Normalerweise verwenden Sie diese Technik, um Instanzwerte für Ihre Anwendung zu speichern (Auswahlen, nicht gespeicherter Text usw.).

30 Stimmen

Besteht die Möglichkeit, dass dies auf dem Telefon funktioniert, aber nicht im Emulator? Ich kann nicht scheinen, um eine nicht-null savedInstanceState zu erhalten.

6 Stimmen

Ich habe eine ArrayList von Punkten, wie man alle Punkte in dieser Array-Liste zu speichern und dann wiederherstellen sie?

513 Stimmen

VORSICHT: Sie müssen super.onSaveInstanceState(savedInstanceState) aufrufen, bevor Sie Ihre Werte zum Bundle hinzufügen, sonst werden sie bei diesem Aufruf gelöscht (Droid X Android 2.2).

457voto

Dave L. Punkte 42559

El savedInstanceState dient nur dazu, den Zustand einer aktuellen Instanz einer Activity zu speichern, z. B. die aktuelle Navigation oder die Auswahlinformationen, so dass eine Activity, die von Android zerstört und neu erstellt wird, wieder so hergestellt werden kann, wie sie vorher war. Siehe die Dokumentation für onCreate y onSaveInstanceState

Für einen längerfristigen Status sollten Sie eine SQLite-Datenbank, eine Datei oder Voreinstellungen verwenden. Siehe Persistenten Zustand speichern .

5 Stimmen

Wann ist savedInstanceState == null und wann ist er nicht null?

7 Stimmen

SavedInstanceState ist null, wenn das System eine neue Instanz Ihrer Aktivität erstellt, und nicht null, wenn sie wiederhergestellt wird.

8 Stimmen

... was die Frage aufwirft wenn muss das System eine neue Instanz von Activity erstellen. Einige Möglichkeiten, eine Anwendung zu beenden, erzeugen kein Bündel, so dass eine neue Instanz erstellt werden muss. Dies ist das grundlegende Problem; es bedeutet, dass man nicht verlassen Sie sich auf auf das Vorhandensein eines Bündels, und muss eine alternative Möglichkeit der dauerhaften Speicherung tun. Der Vorteil von onSave/onRestoreInstanceState ist, dass es ein Mechanismus ist, mit dem das System Abrupt , ohne viel Systemressourcen zu verbrauchen. Es ist also gut, das zu unterstützen, sowie persistente Speicherung für mehr anmutige Beendigung von App haben.

446voto

Steve Moseley Punkte 5662

Beachten Sie, dass es sich um nicht sicher in der Anwendung onSaveInstanceState y onRestoreInstanceState für persistente Daten nach die Dokumentation zur Aktivität .

In dem Dokument heißt es (im Abschnitt "Lebenszyklus der Aktivitäten"):

Beachten Sie, dass es wichtig ist, die persistente Daten in onPause() stattdessen von onSaveInstanceState(Bundle) denn letzteres ist nicht Teil der Lebenszyklus-Callbacks ist und daher nicht nicht in jeder Situation aufgerufen wird, wie in der Dokumentation beschrieben.

Mit anderen Worten: Legen Sie Ihren Speicher-/Wiederherstellungscode für persistente Daten in onPause() y onResume() !

Zur weiteren Klärung, hier ist die onSaveInstanceState() Dokumentation:

Diese Methode wird aufgerufen, bevor eine Aktivität beendet werden kann, damit sie bei ihrer wenn sie irgendwann in der Zukunft zurückkommt, ihren Zustand wiederherstellen kann. Für Beispiel: Aktivität B wird vor Aktivität A gestartet, und zu einem bestimmten Zeitpunkt Aktivität A beendet wird, um Ressourcen zurückzugewinnen, hat Aktivität A die Möglichkeit, den aktuellen Zustand ihrer Benutzeroberfläche mit dieser Methode zu speichern, so dass, wenn der Benutzer zu Aktivität A zurückkehrt, der Zustand der Benutzeroberfläche wiederhergestellt werden kann über onCreate(Bundle) o onRestoreInstanceState(Bundle) .

58 Stimmen

Nur um es kurz zu machen: Es ist auch nicht unsicher. Das hängt einfach davon ab, was Sie bewahren wollen und wie lange, was @Bernard in seiner ursprünglichen Frage nicht ganz klar ist. InstanceState ist perfekt für die Erhaltung des aktuellen UI-Zustands (Daten, die in Steuerelemente eingegeben werden, aktuelle Positionen in Listen und so weiter), während Pause/Resume die einzige Möglichkeit für eine langfristige persistente Speicherung ist.

31 Stimmen

Dies sollte heruntergestuft werden. Es ist nicht sicher, on(Save|Restore)InstanceState wie Lifecycle-Methoden zu verwenden (d.h. in ihnen etwas anderes zu tun als den Zustand zu speichern/ wiederherzustellen). Sie sind perfekt für das Speichern/Wiederherstellen von Zuständen geeignet. Auch, wie wollen Sie zu speichern / wiederherstellen Zustand in onPause und onResume? Sie erhalten keine Bundles in diesen Methoden, die Sie verwenden können, so müssten Sie einige andere Zustand speichern, in Datenbanken, Dateien, etc. verwenden, die dumm ist.

148 Stimmen

Wir sollten diese Person nicht herabstimmen, zumindest hat er sich bemüht, die Dokumentation durchzugehen, und ich denke, wir sind hier, um eine sachkundige Gemeinschaft aufzubauen und uns gegenseitig zu helfen, nicht um herabzustimmen. also 1 Stimme hoch für die Mühe und ich bitte euch, nicht herabzustimmen, sondern hochzustimmen oder nicht zu stimmen.... diese Person die Verwirrung zu beseitigen, die man haben möchte, wenn man die Dokumentation durchgeht. 1 Stimme nach oben :)

223voto

Mein Kollege hat einen Artikel geschrieben, in dem der Anwendungsstatus auf Android-Geräten erklärt wird, einschließlich Erklärungen zum Aktivitätslebenszyklus und zu Statusinformationen, zum Speichern von Statusinformationen und zum Speichern im Status Bundle y SharedPreferences . Sehen Sie es sich hier an .

Der Artikel behandelt drei Ansätze:

Speicherung lokaler Variablen/UI-Steuerungsdaten für die Lebensdauer der Anwendung (d.h. vorübergehend) unter Verwendung eines Instanzstatusbündels

[Code sample – Store state in state bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

Speicherung lokaler Variablen/UI-Steuerungsdaten zwischen Anwendungsinstanzen (d.h. dauerhaft) unter Verwendung gemeinsamer Einstellungen

[Code sample – store state in SharedPreferences]
@Override
protected void onPause()
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store
  // Commit to storage
  editor.commit();
}

Aufrechterhaltung von Objektinstanzen im Speicher zwischen Aktivitäten innerhalb der Anwendungslebensdauer unter Verwendung einer beibehaltenen Nicht-Konfigurationsinstanz

[Code sample – store object instance]
private cMyClassType moInstanceOfAClass; // Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance()
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

3 Stimmen

@MartinBelcher-Eigo Artikel sagt über Daten in SharedPreferences, dass "diese Daten in die Datenbank auf dem Gerät geschrieben werden." Ich glaube, dass die Daten in einer Datei im Verzeichnis der App im Dateisystem gespeichert werden.

2 Stimmen

@Tom SharefPrefs Daten werden in eine xml-Datei geschrieben. Ist Xml eine Art von Datenbank? Ich würde sagen, das ist sie ;)

159voto

Mike Repass Punkte 6747

Dies ist ein klassisches Problem bei der Android-Entwicklung. Hier gibt es zwei Probleme:

  • Es gibt einen subtilen Fehler im Android-Framework, der die Verwaltung des Anwendungsstapels während der Entwicklung erheblich erschwert, zumindest bei älteren Versionen (ich bin nicht ganz sicher, ob/wann/wie er behoben wurde). Ich werde diesen Fehler weiter unten besprechen.
  • Der "normale" oder beabsichtigte Weg, dieses Problem zu lösen, ist mit der Dualität von onPause/onResume und onSaveInstanceState/onRestoreInstanceState ziemlich kompliziert

Wenn ich mir all diese Threads ansehe, vermute ich, dass die Entwickler häufig über diese beiden unterschiedlichen Themen gleichzeitig sprechen ... daher die ganze Verwirrung und die Berichte über "das funktioniert bei mir nicht".

Erstens, um das "beabsichtigte" Verhalten zu klären: onSaveInstance und onRestoreInstance sind anfällig und nur für den vorübergehenden Zustand. Die beabsichtigte Verwendung (soweit ich das beurteilen kann) besteht darin, die Wiederherstellung der Aktivität zu behandeln, wenn das Telefon gedreht wird (Änderung der Ausrichtung). Mit anderen Worten, die beabsichtigte Verwendung ist, wenn Ihre Aktivität logisch immer noch "oben" ist, aber dennoch vom System neu instanziiert werden muss. Das gespeicherte Bundle wird nicht außerhalb des Prozesses/Speichers/ GC Sie können sich also nicht wirklich darauf verlassen, wenn Ihre Aktivität in den Hintergrund tritt. Ja, vielleicht überlebt der Speicher Ihrer Aktivität den Wechsel in den Hintergrund und entgeht GC, aber das ist nicht zuverlässig (und auch nicht vorhersehbar).

Wenn Sie also ein Szenario haben, in dem es einen bedeutsamen "Benutzerfortschritt" oder einen Zustand gibt, der zwischen den "Starts" Ihrer Anwendung erhalten bleiben soll, ist die Anleitung, onPause und onResume zu verwenden. Sie müssen einen persistenten Speicher selbst auswählen und vorbereiten.

Aber - es gibt einen sehr verwirrenden Fehler, der all dies verkompliziert. Details sind hier:

Wenn Ihre Anwendung mit dem SingleTask-Flag gestartet wird und Sie sie später über den Startbildschirm oder das Startmenü starten, wird durch den anschließenden Aufruf eine NEUE Aufgabe erstellt ... Sie haben effektiv zwei verschiedene Instanzen Ihrer Anwendung, die denselben Stapel bewohnen ... was sehr schnell sehr seltsam wird. Dies scheint zu passieren, wenn Sie Ihre Anwendung während der Entwicklung starten (d.h. von Eclipse o IntelliJ ), so dass Entwickler häufig mit diesem Problem konfrontiert werden. Aber auch durch einige der Aktualisierungsmechanismen des App-Stores (so dass es sich auch auf Ihre Benutzer auswirkt).

Ich habe mich stundenlang durch diese Threads gekämpft, bevor ich erkannte, dass mein Hauptproblem dieser Fehler war und nicht das beabsichtigte Verhalten des Frameworks. Ein großartiger Bericht und Abhilfe (UPDATE: siehe unten) scheint von Benutzer @kaciula in dieser Antwort zu stammen:

Verhalten beim Drücken der Home-Taste

UPDATE Juni 2013 : Monate später habe ich endlich die "richtige" Lösung gefunden. Sie brauchen keine zustandsabhängigen StartApp-Flags selbst zu verwalten. Sie können dies vom Framework erkennen und entsprechend ausweichen. Ich verwende dies in der Nähe des Beginns meiner LauncherActivity.onCreate:

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X