Frage

Ich habe auf der Android SDK-Plattform gearbeitet, und es ist ein wenig unklar, wie eine Anwendung des Staat zu retten. So gegeben, diese kleinere Nachrüsten des ‚Hallo, Android‘ Beispiel:

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);
  }
}

dachte ich, es wäre für den einfachsten Fall genug sein, aber es antwortet immer mit der ersten Nachricht, egal wie ich navigieren weg von der App.

Ich bin sicher, dass die Lösung so einfach wie übergeordnete onPause oder so ähnlich ist, aber ich habe weg für 30 Minuten in der Dokumentation wurde Stossen oder so und habe nichts offensichtlich gefunden.

War es hilfreich?

Lösung

Sie müssen onSaveInstanceState(Bundle savedInstanceState) außer Kraft zu setzen und die Anwendungszustandswerte schreiben Sie an den Bundle Parameter wie folgt geändert werden soll:

@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 zu speichern ( „Name-Wert-Paar“) Karte, und es wird in geben bekommen onCreate() und auch onRestoreInstanceState(), wo man dann die Werte wie folgt extrahieren würde:

@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");
}

Sie würden in der Regel diese Technik verwenden, um beispielsweise Werte für Ihre Anwendung zu speichern (Auswahl, nicht gespeicherte Text usw.).

Andere Tipps

Die savedInstanceState ist nur für den Zustand mit einer aktuellen Instanz einer Aktivität zugeordnet zu speichern, zum Beispiel aktueller Navigation oder Auswahl Info, so dass, wenn Android zerstört und erstellt eine Aktivität, kann es wieder kommen, wie es vorher war. Lesen Sie die Dokumentation für onCreate und onSaveInstanceState

Für mehr lange gelebt Zustand, sollten Sie eine SQLite-Datenbank verwenden, um eine Datei oder Präferenzen. Siehe Speichern von persistenten Zustand .

Beachten Sie, dass es ist nicht sicher onSaveInstanceState und onRestoreInstanceState für persistente Daten zu verwenden , entsprechend der Dokumentation auf Aktivitätszustände in http://developer.android.com/reference/android/app/Activity.html .

In dem Dokument heißt (in dem 'Activity Lifecycle' Abschnitt):

  

Beachten Sie, dass es wichtig ist, zu retten   persistente Daten in onPause() statt   von onSaveInstanceState(Bundle)   weil der spätere Zeitpunkt ist nicht Teil der   Lifecycle-Rückrufe, wird so nicht sein   in jeder Situation genannt, wie beschrieben   in seiner Dokumentation.

Mit anderen Worten, setzen Sie Ihre Speichern / Wiederherstellen der Code für persistente Daten in onPause() und onResume()!

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

  

Diese Methode wird aufgerufen, bevor eine Tätigkeit getötet werden kann, so dass, wenn es   einige Zeit in der Zukunft kommt zurück, um es seinen Zustand wiederherstellen. Zum   Wenn beispielsweise Aktivität B vor Aktivität A gestartet, und in einigen   Punkt Aktivität A wird getötet Mittel zurückzufordern, wird Aktivität A haben   eine Chance, den aktuellen Status seiner Benutzeroberfläche über diese zu speichern   Methode, so dass, wenn der Benutzer kehrt zu A-Aktivität, der Zustand der   Benutzeroberfläche kann über onCreate(Bundle) gestellt werden oder   onRestoreInstanceState(Bundle).

Mein Kollege hat einen Artikel erklärt Anwendungszustand auf Android-Geräten, einschließlich Erklärungen auf Aktivität Lifecycle und Zustandsinformationen, wie Statusinformationen zu speichern und zu Speicher in dem Zustand Bundle und SharedPreferences und an hier einen Blick darauf werfen.

Der Artikel umfasst drei Ansätze:

Speicher lokale Variablen / UI-Steuerdaten für die Anwendungslebensdauer (d.h. zeitweise) unter Verwendung einer Instanz Zustand Bündel

[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);
}

Shop lokales Variable / UI-Steuerdaten zwischen Anwendungsinstanzen (das heißt dauerhaft) unter Verwendung gemeinsame 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();
}

Keeping Objektinstanzen lebendig in Erinnerung zwischen Aktivitäten im Anwendungslebensdauer einer beibehaltene nicht-Konfigurationsinstanz mit

[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();
}

Dies ist ein klassisches ‚Gotcha‘ von Android Entwicklung. Es gibt zwei Probleme hier:

  • Es ist ein subtiler Fehler Android-Framework, das Anwendungs-Stack-Management während der Entwicklung stark erschwert, zumindest auf älteren Versionen (nicht ganz sicher, ob / wann / wie es behoben wurde). Ich werde diesen Fehler unten diskutieren.
  • Die ‚normale‘ oder beabsichtigte Art und Weise, dieses Problem zu verwalten ist, sich ziemlich kompliziert mit der Dualität von onPause / onResume und onSaveInstanceState / onRestoreInstanceState

Browsing in all diesen Themen, ich vermute, dass ein großer Teil der Zeit Entwickler über diese beiden verschiedenen Themen sprechen gleichzeitig ... damit alle Verwirrung und Berichte von „dies nicht für mich arbeiten.“

Zuerst das "bestimmt das Verhalten zu klären: onSaveInstance und onRestoreInstance ist zerbrechlich und nur für Übergangszustand. Die beabsichtigte Verwendung (AFAIK) ist Aktivität Erholung zu handhaben, wenn das Telefon (Ausrichtung ändern) gedreht wird. Mit anderen Worten, ist die beabsichtigte Verwendung, wenn Ihre Aktivität noch logisch ‚oben‘, aber immer noch muß vom System reinstantiated werden. Die gespeicherte Bundle ist nicht außerhalb des Prozesses / Speicher / gc anhielt, so dass Sie nicht wirklich darauf verlassen können, wenn Ihre Aktivität in den Hintergrund geht. Ja, vielleicht Speicher Ihre Aktivität werden seine Reise in den Hintergrund überleben und GC entkommen, aber das ist nicht zuverlässig (noch ist es vorhersehbar).

Wenn Sie also ein Szenario, in dem es sinnvoll ist ‚user Fortschritt‘ oder Zustand, der zwischen ‚startet‘ Ihre Anwendung beibehalten werden soll, die Führung ist onPause und onResume zu verwenden. Sie müssen einen persistenten Speicher selbst wählen und vorzubereiten.

ABER - es ist ein sehr verwirrend Fehler, der all dies erschwert. Details sind hier:

http://code.google.com/p/android/ Fragen / detail? id = 2373

http://code.google.com/p/android/ Fragen / detail? id = 5277

Grundsätzlich, wenn Ihre Anwendung mit der SingleTask Flagge gestartet wird, und dann später auf Sie es aus dem Home-Bildschirm oder Startmenü starten, dann, dass nachfolgende Aufruf eine neue Aufgabe erstellen wird ... werden Sie zwei haben effektiv verschiedene Instanzen Ihre App den gleichen Stapel bewohnen ... die sehr schnell sehr seltsam bekommt. Dies scheint zu passieren, wenn Sie Ihre Anwendung während der Entwicklung starten (das heißt von Eclipse oder IntelliJ), so dass Entwickler in diesen viel laufen. Aber auch Update-Mechanismen durch einige der App-Store (so dass es Auswirkungen auf Ihre Benutzer als auch).

Ich kämpfte durch diese Fäden stundenlang, bis ich begriff, dass mein Hauptproblem dieser Fehler war, nicht das richtige Rahmen Verhalten. Eine große writeup und Abhilfe (UPDATE: siehe unten) scheint von Benutzer @kaciula in dieser Antwort zu sein:

Home-Taste drücken Verhalten

UPDATE Juni 2013 : Monate später habe ich endlich die 'richtige' Lösung. Sie brauchen keine Stateful startedApp Flaggen selbst zu verwalten, können Sie diese aus dem Rahmen erkennen und entsprechend bürgen. Ich benutze diese am Anfang 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;
    }
}

onSaveInstanceState wird aufgerufen, wenn der Systemspeicher benötigt und tötet eine Anwendung. Es wird nicht aufgerufen, wenn der Benutzer nur die Anwendung geschlossen wird. Deshalb denke ich, Anwendungszustand sollte auch in onPause gespeichert werden es bis zu einem gewissen persistenten Speicher wie Preferences oder Sqlite gespeichert werden sollen

Beide Methoden sind nützlich und gültig und beide sind am besten geeignet für verschiedene Szenarien:

  1. Der Benutzer die Anwendung beendet und wieder öffnet es zu einem späteren Zeitpunkt, aber die Anwendung muss Daten aus der letzten Sitzung erneut zu laden -. Dies erfordert einen permanenten Speicher Ansatz wie SQLite mit
  2. Der Benutzer schaltet Anwendung und dann kommt wieder auf die ursprüngliche und will abholen, wo sie aufgehört haben - Speichern und Wiederherstellen von Bündeldaten (wie Anwendungszustandsdaten) in onSaveInstanceState() und onRestoreInstanceState() ist in der Regel ausreichend
  3. .

Wenn Sie die Zustandsdaten in persistent speichern, kann es in einem onResume() oder onCreate() (oder tatsächlich auf jedem Lebenszyklus-Aufruf) neu geladen werden. Dies kann oder kann nicht Verhalten erwünscht sein. Wenn Sie es in einem Bündel in einem InstanceState speichern, dann ist es vorübergehend und eignet sich nur für Daten zur Verwendung in der gleichen Benutzer-Session Speichern (Ich verwende den Begriff Sitzung lose), aber nicht zwischen ‚Sitzungen‘.

Es ist nicht, dass ein Ansatz ist besser als die andere, wie alles, ist es nur wichtig zu verstehen, welches Verhalten Sie benötigen und den am besten geeigneten Ansatz zu wählen.

Sparzustand ist eine Flickschusterei bestenfalls so weit es mich betrifft. Wenn Sie persistente Daten speichern müssen, verwenden Sie einfach einen SQLite Datenbank. Android macht es SOOO einfach.

So etwas wie folgt aus:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close() {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType) {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true){
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue){

        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

Ein einfacher Anruf danach

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;

Ich denke, dass ich die Antwort gefunden. Lassen Sie mich sagen, was ich in einfachen Worten getan haben:

Angenommen, ich habe zwei Aktivitäten, tätigkeit1 und activity2 und ich von tätigkeit1 zu activity2 bin Navigation (ich habe einige Werke in activity2 getan) und wieder zurück zur Aktivität 1, indem Sie auf eine Schaltfläche in tätigkeit1 klicken. Jetzt in dieser Phase wollte ich zurück nach activity2 gehen, und ich möchte, dass meine activity2 im gleichen Zustand sehen, wenn ich das letzte Mal activity2 links.

Für das obige Szenario, was ich getan habe, ist, dass im Manifest ich einige Änderungen wie folgt aus:

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

Und in der tätigkeit1 auf die Schaltfläche Click-Ereignis ich so getan haben:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

Und in activity2 auf die Schaltfläche Click-Ereignis habe ich so gemacht:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

Jetzt passieren, was wird, ist, dass, was auch immer die Veränderungen, die wir in der activity2 gemacht haben nicht verloren, und wir können in dem gleichen Zustand sehen activity2 wie wir vorher verlassen.

Ich glaube, das ist die Antwort, und dies funktioniert gut für mich. Korrigieren Sie mich, wenn ich falsch bin.

onSaveInstanceState() für transiente Daten (wieder in onCreate() / onRestoreInstanceState()), onPause() für persistente Daten (in onResume() gestellt). Von Android technischen Ressourcen:

  

onSaveInstanceState () wird von Android genannt, wenn die Aktivität gestoppt wird und möglicherweise getötet werden, bevor es wieder aufgenommen wird! Das bedeutet, es jeden Zustand gespeichert werden sollte notwendig, um die gleichen Bedingungen erneut zu initialisieren, wenn die Aktivität neu gestartet wird. Es ist das Gegenstück zum onCreate () -Methode, und in der Tat das savedInstanceState Bundle übergeben zu onCreate () die gleiche Bundle, das Sie als outstate im onSaveInstanceState () -Methode konstruieren.

     

onPause () und onResume () sind auch kostenlose Methoden. onPause () aufgerufen werden immer, wenn die Aktivität endet, auch wenn wir angestiftet, dass (mit einem Finish () Aufruf zum Beispiel). Diese werden wir nutzen die aktuelle Notiz zurück in die Datenbank zu speichern. Gute Praxis ist es, alle Ressourcen freizusetzen, die während eines onPause () als auch gelöst werden können, nehmen weniger Ressourcen, wenn im passiven Zustand.

Wirklich onSaveInstance Zustand callen, wenn die Aktivität in den Hintergrund geht

Zitat von der Dokumentation: „Das Verfahren onSaveInstanceState(Bundle) wird aufgerufen, bevor die Aktivität in einem solchen Zustand versetzt Hintergrund“

reduzieren helfen vorformulierten wir folgende interface und class verwenden zum Lesen / Schreiben auf einen Bundle zum Beispiel Zustand zu speichern.


Erstellen Sie zunächst eine Schnittstelle, die verwendet werden, werden Ihre Instanzvariablen zu annotieren:

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 {

}

Dann eine Klasse erstellen, wo Reflexion verwendet werden Werte auf das Bündel 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 Nutzung:

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);
    }

}

Hinweis: Dieser Code wurde aus einer Bibliothek Projekt mit dem Namen angepasst AndroidAutowire , die unter der Lizenz ist < a href = "https://raw.githubusercontent.com/CardinalNow/AndroidAutowire/master/LICENSE"> MIT-Lizenz .

Inzwischen habe ich im Allgemeinen nicht mehr verwenden

Bundle savedInstanceState & Co

Der Lebenszyklus ist für die meisten Aktivitäten zu kompliziert und nicht erforderlich.

Und Google erklärt selbst, ist es nicht einmal zuverlässig.

Mein Weg ist alle Änderungen sofort in den Einstellungen zu speichern:

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

In gewisser Weise SharedPreferences wie Bundles ähnlich arbeiten. Und natürlich und zunächst solche Werte von Einstellungen gelesen werden.

Im Fall von komplexen Daten können Sie SQLite verwenden, anstatt Einstellungen zu verwenden.

Wenn dieses Konzept anwenden, die Aktivität weiterhin nur den zuletzt gespeicherten Zustand zu verwenden, unabhängig davon, ob es sich um eine anfängliche offen mit Neustarts zwischendurch oder ein wieder öffnen aufgrund des Rück Stapel.

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

Ihre Aktivität wird nur mit einem Zustand Bündel neu erstellt werden, wenn:

  • Konfigurationsänderungen wie die Ausrichtung oder Telefonsprache zu ändern, die können eine neue Aktivitätsinstanz erfordert geschaffen werden.
  • Sie kehren in die App aus dem Hintergrund, nachdem das OS die Aktivität zerstört hat.

Android wird Hintergrundaktivitäten, wenn unter Speicherdruck zerstören oder nachdem sie über einen längeren Zeitraum im Hintergrund sind.

Wenn Ihr Hallo Welt Beispiel gibt Testen gibt ein paar Möglichkeiten, um die Aktivität zu verlassen und zurück.

  • Wenn Sie die Zurück-Taste die Aktivität drücken beendet. Re-Start der App ist eine brandneue Instanz. Sie sind nicht aus dem Hintergrund der Wiederaufnahme überhaupt.
  • Wenn Sie die Home-Taste drücken oder den Task Switcher verwenden die Aktivität in den Hintergrund gehen. Wenn wieder an die Anwendung navigiert onCreate wird nur aufgerufen werden, wenn die Aktivität zerstört werden mußte.

In den meisten Fällen, wenn Sie nur nach Hause drücken und dann startet die App erneut die Aktivität wird nicht neu erstellt werden müssen. Es existiert bereits im Speicher so onCreate () nicht aufgerufen werden.

Es ist eine Option unter Einstellungen -> Entwickleroptionen genannt „Do Aktivitäten nicht halten“. Wenn es aktiviert Android wird immer Aktivitäten zerstören und sie neu erstellen, wenn sie hintergründig sind. Dies ist eine großartige Option aktiviert zu lassen, wenn die Entwicklung, weil es das Worst-Case-Szenario simuliert. (Eine niedrige Speichergerät Ihre Aktivitäten die ganze Zeit das Recycling).

Die anderen Antworten wertvoll sind, dass sie lehren Sie die richtigen Wege Zustand zu speichern, aber ich habe nicht das Gefühl sie wirklich beantwortet, warum Ihr Code nicht in der Art und Weise arbeiten Sie erwartet.

Die onSaveInstanceState(bundle) und onRestoreInstanceState(bundle) Methoden sind für die Daten Persistenz nur nützlich während der Bildschirm (Orientierungsänderung) dreht.
Sie sind nicht einmal gut, während das Umschalten zwischen Anwendungen (da die onSaveInstanceState() Methode aufgerufen wird, aber onCreate(bundle) und onRestoreInstanceState(bundle) nicht wieder aufgerufen.
Für mehr Ausdauer Verwendung gemeinsamer Vorlieben. lesen Sie diesen Artikel

Mein Problem war, dass ich brauchte Ausdauer nur während des Anwendungslebensdauer (das heißt eine einzige Ausführung einschließlich dem Starten andere Teilaktivitäten innerhalb der gleichen App und Drehen die Vorrichtung usw.). Ich habe versucht, verschiedene Kombinationen der oben genannten Antworten, aber nicht bekommen, was ich in allen Situationen wollte. Am Ende, was für mich gearbeitet wurde einen Verweis auf die savedInstanceState während onCreate zu erhalten:

mySavedInstanceState=savedInstanceState;

und verwenden, die den Inhalt meiner Variable zu erhalten, wenn ich es brauchte, entlang der Linien von:

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

Ich verwende onSaveInstanceStateand onRestoreInstanceState wie oben vorgeschlagen, aber ich denke, ich könnte auch oder alternativ meine Methode verwenden, um die Variable zu speichern, wenn sie sich ändert (zum Beispiel unter Verwendung von putBoolean)

Obwohl die akzeptierte Antwort richtig ist, gibt es eine schnellere und einfachere Methode der Aktivitätszustand auf Android speichern eine Bibliothek namens Icepick . Icepick ist eine Annotation-Prozessor, der Pflege aller Standardcode nimmt verwendet in Speichern und Wiederherstellen Zustand für Sie.

Doing so etwas wie dies mit Icepick:

class MainActivity extends Activity {
  @State String username; // These will be automatically saved and restored
  @State String password;
  @State int age;

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

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

Ist das gleiche wie dies zu tun:

class MainActivity extends Activity {
  String username;
  String password;
  int age;

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("MyString", username);
    savedInstanceState.putString("MyPassword", password);
    savedInstanceState.putInt("MyAge", age); 
    /* remember you would need to actually initialize these variables before putting it in the
    Bundle */
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    username = savedInstanceState.getString("MyString");
    password = savedInstanceState.getString("MyPassword");
    age = savedInstanceState.getInt("MyAge");
  }
}

Icepick wird mit einem Objekt arbeiten, die ihren Zustand mit einem Bundle speichert.

Wenn eine Aktivität erstellt wird es onCreate () -Methode aufgerufen wird.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

savedInstanceState ist ein Ziel der Bundle-Klasse, die zum ersten Mal Null ist, aber es enthält Werte, wenn es neu erstellt wird. So speichern Aktivität des Staates müssen Sie außer Kraft setzen onSaveInstanceState ().

   @Override
    protected void onSaveInstanceState(Bundle outState) {
      outState.putString("key","Welcome Back")
        super.onSaveInstanceState(outState);       //save state
    }

setzen Sie Ihre Werte in "outstate" Bundle-Objekt wie outState.putString ( "Schlüssel", "Welcome Back") und speichern von Super aufrufen. Wenn Aktivität zerstört wird es Zustand in Bundle-Objekt gerettet werden und kann in onCreate () oder onRestoreInstanceState () nach Erholung wieder hergestellt werden. Bundle erhalten in onCreate () und onRestoreInstanceState () sind gleich.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

          //restore activity's state
         if(savedInstanceState!=null){
          String reStoredString=savedInstanceState.getString("key");
            }
    }

oder

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }

Es gibt grundsätzlich zwei Möglichkeiten, um diese Änderung zu implementieren.

  1. mit onSaveInstanceState() und onRestoreInstanceState().
  2. Im Manifest android:configChanges="orientation|screenSize".

Ich empfehle zweite Methode wirklich nicht zu verwenden. Da in einem meiner Erfahrung war es verursacht Hälfte der Vorrichtung Bildschirm schwarz, während von Hoch- auf Querformat drehen und umgekehrt.

erste Methode, die oben erwähnten Verwendung, können wir Daten bestehen bleiben, wenn Ausrichtung geändert wird oder eine Konfigurationsänderung geschieht. Ich kenne einen Weg, in dem Sie jede Art von Daten innerhalb savedInstance Statusobjekt speichern kann.

Beispiel: ein Fall betrachtet, wenn Sie Json Objekt bestehen bleiben sollen. erstellen Sie eine Modellklasse mit Getter und Setter.

class MyModel extends Serializable{
JSONObject obj;

setJsonObject(JsonObject obj)
{
this.obj=obj;
}

JSONObject getJsonObject()
return this.obj;
} 
}

Jetzt in Ihrer Tätigkeit in onCreate und onSaveInstanceState Verfahren folgendes tun. Es wird wie folgt aussehen:

@override
onCreate(Bundle savedInstaceState){
MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
JSONObject obj=data.getJsonObject();
//Here you have retained JSONObject and can use.
}


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Obj is some json object 
MyModel dataToSave= new MyModel();
dataToSave.setJsonObject(obj);
oustate.putSerializable("yourkey",dataToSave); 

}

Hier ist ein Kommentar von Steve Moseley 's Antwort (von ToolmakerSteve ), die Dinge relativiert (im ganzen onSaveInstanceState vs onPause, Ostkosten vs West-Kosten-Saga )

  

@VVK - ich teilweise nicht einverstanden ist. Einige Möglichkeiten des Verlassens eine App nicht Trigger   onSaveInstanceState (osis). Dies begrenzt die Nützlichkeit von OSIS. Es ist   stützenswert, für eine minimale OS-Ressourcen, aber wenn eine App will   Rückkehr der Benutzer den Zustand sie in waren, egal, wie die App war   verlassen, ist es notwendig, stattdessen einen persistenten Speicher Ansatz zu verwenden.    Ich verwende onCreate für Bündel zu überprüfen, und wenn es fehlt, wird dann prüfen    persistenten Speicher. Diese zentralisiert die Entscheidungsfindung. ich kann   erholen sich von einem Absturz oder Zurück-Taste Ausgang oder benutzerdefinierten Menüpunkt Exit oder   zurück Benutzer Bildschirm war auf viele Tage später. - ToolmakerSteve September   19 '15 um 10:38

Kotlin Code:

speichern:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState.apply {
        putInt("intKey", 1)
        putString("stringKey", "String Value")
        putParcelable("parcelableKey", parcelableObject)
    })
}

und dann in onCreate() oder onRestoreInstanceState()

    val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
    val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
    val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable

Standardwerte hinzufügen, wenn Sie wollen Optionals nicht haben

Um Aktivitätszustandsdaten in onCreate() gespeichert bekommen, zuerst müssen Sie durch zwingende SaveInstanceState(Bundle savedInstanceState) Methode, um Daten in savedInstanceState speichern.

Wenn Aktivität zerstören SaveInstanceState(Bundle savedInstanceState) Methode aufgerufen wird und Sie dort speichern Daten, die Sie speichern möchten. Und Sie erhalten diese in onCreate(), wenn die Aktivität neu zu starten. (SavedInstanceState wird nicht null sein, da Sie einige Daten darin gespeichert haben, bevor die Aktivität zerstört werden)

Einfache schnell, dieses Problem zu lösen, wird unter Verwendung von icepick

Als erstes Setup der Bibliothek in app/build.gradle

repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

Jetzt wollen wir lesen Sie in diesem Beispiel unten, wie Zustand in Aktivität speichern

public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

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

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

Es funktioniert für Aktivitäten, Fragmente oder jedes Objekt, das seinen Zustand auf einem Bündel serialisiert muss (zum Beispiel Mörtel des ViewPresenters)

Icepick kann auch den Instanzstatus Code für benutzerdefinierte Ansichten erzeugen:

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}

Nicht sicher, ob meine Lösung ist verpönt oder nicht, aber ich benutze einen gebundenen Service Ansichtsmodell Zustand zu verharren. Egal, ob Sie im Speicher ablegen im Dienst oder anhalten und Abrufen aus einer SQLite-Datenbank hängt von Ihren Anforderungen. Dies ist, was Dienstleistungen jegliche Geschmacks zu tun, sie bieten Dienstleistungen wie die Aufrechterhaltung logischen Anwendungszustandes und abstrakte gemeinsame Unternehmen.

Aufgrund der Speicher- und Verarbeitungs Zwänge auf mobilen Geräten, behandle ich Android Ansichten auf eine ähnliche Art und Weise zu einer Webseite. Die Seite nicht aufrechterhalten Zustand, es ist lediglich eine Komponente Präsentationsschicht, deren einziger Zweck es ist, Anwendungszustand zu präsentieren und Benutzereingaben zu akzeptieren. Aktuelle Trends in der Web-App-Architektur die Verwendung des alten Model, View Controller (MVC) Muster verwenden, wo die Seite der Ansicht ist, Domain-Daten ist das Modell, und der Controller sitzt hinter einem Web-Service. Das gleiche Muster kann in Android mit der Ansicht Wesen verwendet werden, gut ... die Ansicht, das Modell ist Ihre Domain-Daten, und der Controller wird als Android gebunden Dienst implementiert. Jedes Mal, wenn Sie eine Ansicht wollen mit dem Controller zu kommunizieren, sie binden, um es auf dem Start / Resume und unbind auf Stopp / Pause.

Dieser Ansatz gibt Ihnen die zusätzlichen Bonus von der Separation of Concern Konstruktionsprinzip in, dass Sie alle Anwendung Geschäftslogik Durchsetzung kann in Ihren Dienst bewegt werden, die duplizierten Logik über mehrere Ansichten reduziert und ermöglicht die Ansicht ein weiteres wichtiges Gestaltungsprinzip durchzusetzen, einzelne Verantwortung.

Jetzt Android bietet Viewmodels Zustand speichern, sollten Sie versuchen, dass anstelle von saveInstanceState zu verwenden.

Was zu speichern und was nicht?

Überhaupt gewundert, warum der Text in den EditText automatisch während einer Orientierungsänderung gespeichert wird? Nun, diese Antwort ist für Sie.

Wenn eine Instanz einer Aktivität wird zerstört und das System erschafft eine neue Instanz (zB Konfigurationsänderung). Es wird versucht, neu erstellen sie eine Reihe von gespeicherten Daten des alten Aktivitätszustandes mit ( Instanz Zustand ).

Instance Zustand ist eine Sammlung von Schlüsselwert gespeicherten Paare in einem Bundle Objekt.

  

Mit dem Standard-System speichert die Ansicht Objekte im Bundle zum Beispiel.

  • Text in EditText
  • Scroll-Position in einem ListView, etc.

Wenn Sie eine andere Variable müssen als Teil des Instanzstatus gespeichert werden, sollten Sie KORREKTUR onSavedInstanceState(Bundle savedinstaneState) Methode.

Zum Beispiel int currentScore in einem GameActivity

Weitere Einzelheiten zum onSavedInstanceState (Bundle savedinstaneState) beim Speichern Daten

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}
  

So versehentlich, wenn Sie vergessen zu nennen   super.onSaveInstanceState(savedInstanceState);the Standardverhalten   wird also Text in EditText nicht funktioniert nicht speichern.

, die für die Wiederherstellung Aktivitätszustand?

wählen
 onCreate(Bundle savedInstanceState)

ODER

onRestoreInstanceState(Bundle savedInstanceState)

Beiden Methoden erhalten das gleiche Bundle-Objekt, so dass es nicht wirklich wichtig, wo Sie Ihre Wiederherstellung der Logik schreiben. Der einzige Unterschied besteht darin, dass in onCreate(Bundle savedInstanceState) Methode erhalten Sie eine NULL-Prüfung geben, während sie nicht im letzteren Fall benötigt wird. Andere Antworten haben bereits Code-Schnipsel. Sie können sie verweisen.

Weitere Einzelheiten zum onRestoreInstanceState (Bundle savedinstaneState)

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from the saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
}
  

Immer super.onRestoreInstanceState(savedInstanceState); rufen, damit das System der Ansicht Hierarchie standardmäßig wiederherstellen

Bonus

Die onSaveInstanceState(Bundle savedInstanceState) wird vom System nur aufgerufen, wenn der Benutzer zurück auf die Aktivität kommen will. Zum Beispiel verwenden Sie App X und Sie plötzlich einen Anruf erhalten. Sie bewegen sich an den Aufrufer App und kommen zurück in die App X. In diesem Fall wird die onSaveInstanceState(Bundle savedInstanceState) Methode aufgerufen wird.

Aber dies berücksichtigen, wenn ein Benutzer die Zurück-Taste drückt. Es wird angenommen, dass der Benutzer nicht beabsichtigt, zurück in die Aktivität zu kommen, also in diesem Fall onSaveInstanceState(Bundle savedInstanceState) wird nicht vom System aufgerufen werden. Punkt ist, sollten Sie alle Szenarien betrachten, während die Daten zu speichern.

Relevante Links:

Demo auf Standardverhalten
Android Offizielle Dokumentation .

Kotlin

Sie müssen onSaveInstanceState und onRestoreInstanceState außer Kraft setzen zu speichern und abrufen Variablen Sie persistent sein wollen

Life Cycle Graph

Shop Variablen

public override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)

    // prepare variables here
    savedInstanceState.putInt("kInt", 10)
    savedInstanceState.putBoolean("kBool", true)
    savedInstanceState.putDouble("kDouble", 4.5)
    savedInstanceState.putString("kString", "Hello Kotlin")
}

Abrufen Variablen

public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}

Ich habe eine bessere Idee. Dies wäre besser, um Ihre Daten zu speichern, ohne erneuten Aufruf onCreate. Sie können es von Aktivität deaktivieren, wenn Ausrichtung ändert.

In Ihrem Manifest:

<activity android:name=".MainActivity"
        android:configChanges="orientation|screenSize">

Edit: Diese Lösung wird nicht funktionieren, wenn Android den Prozess aufgrund des geringen Speichers tötet. wenn Sie Daten benötigen, um sicher zu halten, müssen Sie SavedInstanceState in diesem Fall verwenden, sonst schlage ich auf diese Weise verwendet wird. kühl und einfach zu bedienen!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top