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.

46voto

u-foka Punkte 657

Wirklich onSaveInstanceState() wird aufgerufen, wenn die Aktivität in den Hintergrund geht.

Zitat aus den Unterlagen: "Diese Methode wird aufgerufen, bevor eine Aktivität beendet werden kann, damit sie, wenn sie irgendwann in der Zukunft zurückkommt, ihren Zustand wiederherstellen kann." Quelle

40voto

Jared Rummler Punkte 36806

Zur Verringerung der Floskeln verwende ich Folgendes interface y class zum Lesen/Schreiben auf einer Bundle zum Speichern des Instanzstatus.


Erstellen Sie zunächst eine Schnittstelle, die zur Kommentierung Ihrer Instanzvariablen verwendet wird:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.FIELD
})
public @interface SaveInstance {

}

Erstellen Sie dann eine Klasse, in der die Reflexion verwendet wird, um Werte im Bundle zu speichern:

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.Serializable;
import java.lang.reflect.Field;

/**
 * Save and load fields to/from a {@link Bundle}. All fields should be annotated with {@link
 * SaveInstance}.</p>
 */
public class Icicle {

    private static final String TAG = "Icicle";

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #load(Bundle, Object)
     */
    public static void save(Bundle outState, Object classInstance) {
        save(outState, classInstance, classInstance.getClass());
    }

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #load(Bundle, Object, Class)
     */
    public static void save(Bundle outState, Object classInstance, Class<?> baseClass) {
        if (outState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    field.setAccessible(true);
                    String key = className + "#" + field.getName();
                    try {
                        Object value = field.get(classInstance);
                        if (value instanceof Parcelable) {
                            outState.putParcelable(key, (Parcelable) value);
                        } else if (value instanceof Serializable) {
                            outState.putSerializable(key, (Serializable) value);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not added to the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #save(Bundle, Object)
     */
    public static void load(Bundle savedInstanceState, Object classInstance) {
        load(savedInstanceState, classInstance, classInstance.getClass());
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #save(Bundle, Object, Class)
     */
    public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) {
        if (savedInstanceState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    String key = className + "#" + field.getName();
                    field.setAccessible(true);
                    try {
                        Object fieldVal = savedInstanceState.get(key);
                        if (fieldVal != null) {
                            field.set(classInstance, fieldVal);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

}

Beispiel für die Verwendung:

public class MainActivity extends Activity {

    @SaveInstance
    private String foo;

    @SaveInstance
    private int bar;

    @SaveInstance
    private Intent baz;

    @SaveInstance
    private boolean qux;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Icicle.load(savedInstanceState, this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Icicle.save(outState, this);
    }

}

Anmerkung: Dieser Code wurde von einem Bibliotheksprojekt namens AndroidAutowire das unter der Lizenz der MIT-Lizenz .

40voto

stefan bachert Punkte 8977

Inzwischen benutze ich im Allgemeinen nicht mehr

Bundle savedInstanceState & Co

Der Lebenszyklus ist für die meisten Tätigkeiten zu kompliziert und nicht notwendig.

Und Google sagt selbst, dass es NICHT einmal zuverlässig ist.

Bei mir werden alle Änderungen sofort in den Einstellungen gespeichert:

 SharedPreferences p;
 p.edit().put(..).commit()

In gewisser Weise funktionieren SharedPreferences ähnlich wie Bundles. Und natürlich müssen solche Werte zunächst aus den Einstellungen gelesen werden.

Bei komplexen Daten können Sie SQLite verwenden, anstatt die Einstellungen zu benutzen.

Bei der Anwendung dieses Konzepts verwendet die Aktivität einfach den zuletzt gespeicherten Zustand, unabhängig davon, ob es sich um ein erstes Öffnen mit zwischenzeitlichen Neustarts oder um ein erneutes Öffnen aufgrund des Backstacks handelt.

34voto

Jared Kells Punkte 6287

Um die ursprüngliche Frage direkt zu beantworten: savedInstancestate ist null, weil Ihre Aktivität nie neu erstellt wird.

Ihre Aktivität wird nur dann mit einem Statusbündel neu erstellt, wenn:

  • Konfigurationsänderungen, wie z. B. die Änderung der Ausrichtung oder der Sprache des Telefons, was die Erstellung einer neuen Aktivitätsinstanz erforderlich machen kann.
  • Sie kehren aus dem Hintergrund zur Anwendung zurück, nachdem das Betriebssystem die Aktivität zerstört hat.

Android zerstört Hintergrundaktivitäten, wenn sie unter Speicherdruck stehen oder über einen längeren Zeitraum im Hintergrund ausgeführt wurden.

Beim Testen Ihres Hallo-Welt-Beispiels gibt es mehrere Möglichkeiten, die Aktivität zu verlassen und zu ihr zurückzukehren.

  • Wenn Sie die Zurück-Taste drücken, ist die Aktivität beendet. Das erneute Starten der Anwendung ist eine ganz neue Instanz. Sie setzen die Aktivität nicht im Hintergrund fort.
  • Wenn Sie die Home-Taste drücken oder den Task-Switcher verwenden, wird die Aktivität in den Hintergrund gestellt. Wenn Sie zurück zur Anwendung navigieren, wird onCreate nur aufgerufen, wenn die Aktivität zerstört werden musste.

In den meisten Fällen, wenn Sie nur auf "Home" drücken und die App erneut starten, muss die Aktivität nicht neu erstellt werden. Sie ist bereits im Speicher vorhanden, sodass onCreate() nicht aufgerufen wird.

Unter Einstellungen -> Entwickleroptionen gibt es eine Option namens "Aktivitäten nicht speichern". Wenn sie aktiviert ist, wird Android Aktivitäten immer zerstören und neu erstellen, wenn sie in den Hintergrund gestellt werden. Dies ist eine großartige Option, die man bei der Entwicklung aktiviert lassen sollte, da sie das Worst-Case-Szenario simuliert. (Ein Gerät mit wenig Speicher, das Ihre Aktivitäten ständig recycelt).

Die anderen Antworten sind insofern wertvoll, als sie Ihnen die korrekte Art und Weise der Zustandsspeicherung beibringen, aber ich hatte nicht das Gefühl, dass sie wirklich beantworteten, WARUM Ihr Code nicht in der von Ihnen erwarteten Weise funktionierte.

31voto

Mahorad Punkte 1238

El onSaveInstanceState(bundle) y onRestoreInstanceState(bundle) Methoden sind nützlich für die Datenpersistenz nur während der Drehung des Bildschirms (Orientierungsänderung).
Sie sind nicht einmal gut, wenn man zwischen Anwendungen wechselt (da die onSaveInstanceState() Methode wird aufgerufen, aber onCreate(bundle) y onRestoreInstanceState(bundle) nicht mehr aufgerufen wird.
Für mehr Persistenz verwenden Sie gemeinsame Einstellungen. diesen Artikel lesen

2 Stimmen

In Ihrem Fall onCreate y onRestoreInstanceState nicht aufgerufen werden, weil die Activity wird nicht zerstört, wenn Sie die Anwendung wechseln, so dass es nicht notwendig ist, etwas wiederherzustellen. Android-Anrufe onSaveInstanceState nur für den Fall, dass die Aktivität später zerstört wird (was beim Drehen des Bildschirms mit 100-prozentiger Sicherheit passiert, weil sich die gesamte Gerätekonfiguration geändert hat und die Aktivität von Grund auf neu erstellt werden muss).

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