Jak zapisać stan działania za pomocą zapisu stanu instancji?

Pracowałem na platformie Android SDK i jest trochę niejasne, jak zapisać stan aplikacji. Tak więc, biorąc pod uwagę to drobne przeprojektowanie przykładu "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);
  }
}

Myślałem, że wystarczy na najprostszy przypadek, ale zawsze odpowiada pierwszą wiadomością, bez względu na to, jak odchodzę od aplikacji.

Jestem pewien, że rozwiązanie jest tak proste, jak nadpisanie onPause Czy coś w tym stylu, ale grzebałem w dokumentacji przez jakieś 30 minut i nie znalazłem nic oczywistego.

Author: Vivek Mishra, 2008-09-30

30 answers

Musisz nadpisać onSaveInstanceState(Bundle savedInstanceState) i zapisać wartości stanu aplikacji, które chcesz zmienić na parametr Bundle w następujący sposób:

@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.
}

Pakiet jest zasadniczo sposobem przechowywania mapy NVP ("para nazwa-wartość"), i zostanie przekazany do onCreate(), a także onRestoreInstanceState(), gdzie następnie wyodrębnisz wartości z aktywności takiej jak:

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

Lub z fragmentu.

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

Zazwyczaj używa się tej techniki do przechowywania wartości instancji aplikacji (selekcji, niezapisanego tekstu, itd.).

 2627
Author: Reto Meier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-24 14:48:39

savedInstanceState służy tylko do zapisywania stanu związanego z bieżącą instancją aktywności, na przykład bieżącą nawigacją lub informacjami o wyborze, tak że jeśli Android zniszczy i odtworzy aktywność, może wrócić tak, jak wcześniej. Zobacz dokumentację dla onCreate oraz onSaveInstanceState

Aby uzyskać bardziej długotrwały stan, rozważ użycie bazy danych SQLite, pliku lub preferencji. Zobacz Zapisywanie Stanu Trwałego .

 440
Author: Dave L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-13 00:24:16

Zauważ, że jest to nie bezpieczne w użyciu onSaveInstanceState i onRestoreInstanceState dla danych trwałych , zgodnie z dokumentacją Stanów aktywności w http://developer.android.com/reference/android/app/Activity.html .

Dokument stwierdza (w sekcji "cykl życia działalności"):

Należy pamiętać, że ważne jest, aby zapisać dane trwałe w onPause() zamiast z onSaveInstanceState(Bundle) ponieważ później nie jest częścią wywołania zwrotne cyklu życia, więc nie będzie wywoływane w każdej sytuacji zgodnie z opisem w swojej dokumentacji.

Innymi słowy, umieść swój kod zapisu / przywracania dla trwałych danych w onPause() i onResume()!

EDIT : dla dalszych wyjaśnień, oto onSaveInstanceState() dokumentacja:

[[9]}metoda ta jest wywoływana zanim czynność może zostać zabita tak, że gdy w przyszłości może przywrócić swój stan. Na przykład, jeśli ćwiczenie B jest uruchamiane przed ćwiczeniem A, a w niektórych punkt działanie A jest zabijane w celu odzyskania zasobów, działanie A będzie miało możliwość zapisania aktualnego stanu interfejsu użytkownika za pomocą tego metoda tak, aby po powrocie użytkownika do aktywności A stan interfejs użytkownika można przywrócić poprzez onCreate(Bundle) lub onRestoreInstanceState(Bundle).
 429
Author: Steve Moseley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 23:04:27

Mój kolega napisał artykuł wyjaśniający stan aplikacji na urządzeniach z Androidem, w tym wyjaśnienia dotyczące cyklu życia aktywności i informacji o stanie, jak przechowywać informacje o stanie i zapisywać do stanu Bundle i SharedPreferences i spójrz tutaj .

Artykuł obejmuje trzy podejścia:

Przechowuj zmienne lokalne/dane sterujące UI przez cały okres życia aplikacji (tj. tymczasowo) przy użyciu pakietu stanu instancji

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

Przechowuje lokalne dane sterujące zmienną/interfejsem użytkownika między przykładami aplikacji (np. na stałe) przy użyciu współdzielonych preferencji

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

Utrzymywanie żywych instancji obiektów w pamięci pomiędzy aktywnościami w czasie życia aplikacji przy użyciu zachowanej instancji nie-konfiguracyjnej

[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();
}
 211
Author: Martin Belcher - AtWrk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-26 16:08:46

Jest to klasyczna' gotcha ' rozwoju Androida. Są tu dwie kwestie:

  • istnieje subtelny błąd systemu Android, który znacznie komplikuje zarządzanie stosem aplikacji podczas tworzenia aplikacji, przynajmniej w starszych wersjach (nie do końca wiem, czy/kiedy / jak został naprawiony). Omówię ten błąd poniżej.
  • "normalny" lub zamierzony sposób radzenia sobie z tym problemem jest, sam w sobie, dość skomplikowany z dualnością onPause/onResume i onSaveInstanceState / onRestoreInstanceState

Przeglądając wszystkie te wątki, podejrzewam, że większość czasu deweloperzy mówią o tych dwóch różnych sprawach jednocześnie ... stąd całe zamieszanie i doniesienia o "to nie działa dla mnie".

Po pierwsze, aby wyjaśnić "zamierzone" zachowanie: onSaveInstance i onRestoreInstance są kruche i tylko dla stanu przejściowego. Zamierzonym zastosowaniem (afaict) jest obsługa rekreacji aktywnej po obróceniu telefonu (zmiana orientacji). Innymi słowy, zamierzone użycie jest wtedy, gdy Twoja aktywność jest nadal logicznie "na szczycie", ale nadal musi zostać ponownie potwierdzona przez system. Zapisany Pakiet nie jest utrzymywany poza procesem / pamięcią / gc, więc nie możesz na tym polegać, jeśli Twoja aktywność przechodzi w tło. Tak, być może pamięć Twojej aktywności przetrwa podróż w tło i ucieczkę z GC, ale nie jest to wiarygodne (ani przewidywalne).

Więc jeśli masz scenariusz, w którym jest znaczące "postępy użytkownika" lub stan, który powinien być utrzymywany pomiędzy "uruchomieniami" Twojej aplikacji, wskazówki dotyczą korzystania z onPause i onResume. Musisz sam wybrać i przygotować trwały sklep.

Ale-jest bardzo mylący błąd, który komplikuje to wszystko. Szczegóły są tutaj:

Http://code.google.com/p/android/issues/detail?id=2373

Http://code.google.com/p/android/issues/detail?id=5277

Zasadniczo, jeśli twój wniosek jest uruchamiany z flagą SingleTask, a później uruchamiasz go z ekranu głównego lub menu launchera, a następnie to kolejne wywołanie utworzy nowe zadanie ... będziesz mieć dwie różne instancje aplikacji zamieszkujące ten sam stos ... co robi się bardzo dziwne, bardzo szybko. Wydaje się, że dzieje się tak, gdy uruchamiasz swoją aplikację podczas rozwoju (np. z Eclipse lub Intellij), więc deweloperzy często na to wpadają. Ale także poprzez niektóre mechanizmy aktualizacji app store (tak więc wpływa użytkowników, jak również).

Zmagałem się z tymi wątkami godzinami, zanim zdałem sobie sprawę, że moim głównym problemem był ten błąd, a nie zamierzone zachowanie frameworka. Wielki writeup i workaround (UPDATE: patrz niżej) wydaje się być od użytkownika @kaciula w tej odpowiedzi:

Home key press behavior

Aktualizacja czerwiec 2013 : miesiące później, w końcu znalazłem "poprawne" rozwiązanie. Nie musisz samodzielnie zarządzać flagami stateful startedApp, możesz / align = "left" / Używam tego na początku mojej pracy."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;
    }
}
 152
Author: Mike Repass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:34

onSaveInstanceState jest wywoływany, gdy system potrzebuje pamięci i zabija aplikację. Nie jest wywoływana, gdy użytkownik po prostu zamyka aplikację. Więc myślę, że stan aplikacji powinien być również zapisany w {[1] } powinien być zapisany w jakimś trwałym magazynie, takim jak Preferences lub Sqlite

 91
Author: Fedor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-20 14:07:53

Obie metody są użyteczne i ważne i obie najlepiej nadają się do różnych scenariuszy:

  1. użytkownik kończy aplikację i otwiera ją ponownie w późniejszym terminie, ale aplikacja musi ponownie załadować dane z ostatniej sesji-wymaga to trwałego przechowywania, takiego jak użycie SQLite.
  2. użytkownik przełącza aplikację, a następnie wraca do oryginału i chce wrócić do miejsca, w którym został przerwany - Zapisz i przywróć dane pakietu (takie jak dane stanu aplikacji) w onSaveInstanceState() i onRestoreInstanceState() jest zazwyczaj odpowiedni.

Jeśli zapisujesz dane Stanu w sposób trwały, można je przeładować w onResume() lub onCreate() (lub w rzeczywistości w dowolnym wywołaniu cyklu życia). Takie zachowanie może, ale nie musi być pożądane. Jeśli przechowujesz go w pakiecie w InstanceState, to jest przejściowy i nadaje się tylko do przechowywania danych do użycia w tej samej sesji użytkownika (używam terminu session luźno), ale nie między "sesjami".

Nie jest tak, że jedno podejście jest lepsze od drugiego, jak wszystko, to jest po prostu ważne, aby zrozumieć, jakiego zachowania potrzebujesz i wybrać najbardziej odpowiednie podejście.

 76
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-12 23:34:47

Stan ratowania jest dla mnie kludżą. Jeśli chcesz zapisać trwałe DANE, po prostu użyj SQLite bazy danych. Android sprawia, że SOOO spokojnie.

Coś takiego:

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

A simple call after that

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;
 72
Author: Mike A.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-11 16:00:25

Chyba znalazłem odpowiedź. Pozwól mi powiedzieć, co zrobiłem w prostych słowach:

Załóżmy, że mam dwie czynności, aktywność1 i aktywność2 i przechodzę od aktywność1 do aktywność2 (wykonałem kilka prac w aktywność2) i ponownie wracam do aktywności 1, klikając przycisk w aktywność1. Teraz na tym etapie chciałem wrócić do activity2 i chcę zobaczyć moją activity2 w tym samym stanie, kiedy ostatni raz opuściłem activity2.

Dla powyższego scenariusza zrobiłem to, że w manifest wprowadziłem takie zmiany:

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

A w aktywności1 na przycisku kliknij Zdarzenie zrobiłem tak:

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

A w activity2 na przycisku click event zrobiłem tak:

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

Teraz nastąpi to, że jakiekolwiek zmiany, które wprowadziliśmy w activity2, nie zostaną utracone i będziemy mogli oglądać activity2 w tym samym stanie, w jakim opuściliśmy wcześniej.

Wierzę, że to jest Odpowiedź i to działa dobrze dla mnie. Popraw mnie, jeśli się mylę.

 64
Author: roy mathew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-31 19:01:15

onSaveInstanceState() dla danych przejściowych (przywracanych w onCreate()/onRestoreInstanceState()), onPause() dla danych trwałych (przywróconych w onResume()). Z zasobów technicznych Androida:

OnSaveInstanceState () jest wywoływana przez Androida, jeśli aktywność jest zatrzymana i może zostać zabita przed jej wznowieniem! Oznacza to, że po ponownym uruchomieniu działania powinien on przechowywać każdy stan niezbędny do ponownej inicjalizacji do tego samego stanu. Jest odpowiednikiem metody onCreate () i w rzeczywistości pakiet savedInstanceState przekazany in to OnCreate() jest Tym Samym pakietem, który konstruujesz jako outState w metodzie onSaveInstanceState ().

OnPause () i onResume () są również komplementarnymi metodami. onpause () jest zawsze wywoływane, gdy aktywność się kończy, nawet jeśli wywołaliśmy to (na przykład wywołaniem finish ()). Użyjemy tego, aby zapisać bieżącą notatkę z powrotem do bazy danych. Dobrą praktyką jest uwalnianie wszelkich zasobów, które mogą być uwalniane podczas onPause (), jak również, aby zająć mniej zasobów, gdy w stan pasywny.

 47
Author: Ixx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Naprawdę onSaveInstanceState() jest wywoływany, gdy aktywność przechodzi w tło.

Cytat z docs: "Metoda ta jest wywoływana przed aktywnością, która może zostać zabita, aby gdy powróci jakiś czas w przyszłości, mogła przywrócić swój stan." Źródło

 43
Author: u-foka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-26 13:59:54

Aby zmniejszyć boilerplate używam następujących interface i class do odczytu/zapisu do Bundle w celu zapisania stanu instancji.


Najpierw Utwórz interfejs, który będzie używany do opisywania zmiennych instancji:

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 {

}

Następnie utwórz klasę, w której reflection będzie używane do zapisywania wartości w pakiecie:

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

}

Przykładowe użycie:

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

}

Uwaga: ten kod został zaadaptowany z projektu biblioteki o nazwie AndroidAutowire, który jest licencjonowane na podstawie licencji MIT .

 39
Author: Jared Rummler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-20 02:04:11

Tymczasem w ogóle nie używam

Bundle savedInstanceState & Co
Cykl życia jest dla większości czynności zbyt skomplikowany i niepotrzebny.

A Google twierdzi, że nie jest nawet wiarygodne.

Moim sposobem jest natychmiastowe zapisywanie zmian w preferencjach:

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

W pewnym sensie Sharedpreferencje działają podobnie jak Pakiety. I naturalnie i na początku takie wartości muszą być odczytywane z preferencji.

W przypadku złożonych danych możesz użyć SQLite zamiast używać preferencje.

Podczas stosowania tej koncepcji działanie nadal używa ostatniego zapisanego stanu, niezależnie od tego, czy było to początkowe otwarcie z ponownym uruchomieniem pomiędzy, czy ponowne otwarcie z powodu tylnego stosu.

 37
Author: stefan bachert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-26 16:17:52

Aby odpowiedzieć bezpośrednio na pierwotne pytanie. savedInstancestate jest null, ponieważ Twoja aktywność nigdy nie jest ponownie tworzona.

Twoja aktywność zostanie odtworzona z pakietem stanu tylko wtedy, gdy:

  • Zmiany konfiguracji, takie jak zmiana orientacji lub języka telefonu, które mogą wymagać utworzenia nowej instancji aktywności.
  • wracasz do aplikacji z tła po tym, jak system operacyjny zniszczył aktywność.

Android zniszczy tło czynności, gdy są pod presją pamięci lub gdy są w tle przez dłuższy czas.

Podczas testowania przykładu hello world istnieje kilka sposobów na opuszczenie i powrót do aktywności.

  • Po naciśnięciu przycisku Wstecz czynność jest zakończona. Ponowne uruchomienie aplikacji to zupełnie nowa instancja. W ogóle nie wracasz z tła.
  • po naciśnięciu przycisku home lub użyciu przełącznika zadań aktywność przejdzie w tło. Kiedy powrót do aplikacji onCreate zostanie wywołany tylko wtedy, gdy aktywność musiała zostać zniszczona.

W większości przypadków, jeśli po prostu naciskasz home, A następnie ponownie uruchamiasz aplikację, Aktywność nie będzie musiała być ponownie utworzona. Istnieje już w pamięci, więc OnCreate() nie zostanie wywołana.

Istnieje opcja w Ustawieniach - > opcje dewelopera o nazwie "nie zachowuj aktywności". Po włączeniu Android zawsze zniszczy działania i odtworzy je, gdy zostaną zablokowane. Jest to świetna opcja, aby pozostawić włączone podczas tworzenia, ponieważ symuluje najgorszy scenariusz. (Urządzenie o niskiej pamięci, które cały czas przetwarza Twoje działania ).

Inne odpowiedzi są cenne, ponieważ uczą cię poprawnych sposobów przechowywania stanu, ale nie czułem, że naprawdę odpowiedzieli, Dlaczego Twój kod nie działa tak, jak oczekiwałeś.

 33
Author: Jared Kells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-21 02:19:16

Metody onSaveInstanceState(bundle) i onRestoreInstanceState(bundle) są przydatne dla trwałości danych tylko podczas obracania ekranu (zmiana orientacji).
Nie są one nawet dobre podczas przełączania między aplikacjami (ponieważ metoda onSaveInstanceState() jest wywoływana, ale onCreate(bundle) i onRestoreInstanceState(bundle) nie jest wywoływana ponownie.
Aby uzyskać większą trwałość, użyj wspólnych preferencji. przeczytaj ten artykuł

 31
Author: Mahorad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-18 04:04:07

Mój problem polegał na tym, że potrzebowałem wytrwałości tylko w czasie życia aplikacji (tj. pojedynczego wykonania obejmującego uruchamianie innych pod-działań w ramach tej samej aplikacji i obracanie urządzenia itp.). Próbowałem różnych kombinacji powyższych odpowiedzi, ale nie dostałem tego, czego chciałem we wszystkich sytuacjach. W końcu udało mi się uzyskać odniesienie do savedInstanceState podczas onCreate: {]}

mySavedInstanceState=savedInstanceState;

I użyj tego, aby uzyskać zawartość mojej zmiennej, gdy jej potrzebowałem, wzdłuż linie:

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

Używam onSaveInstanceState i onRestoreInstanceState jak sugerowano powyżej, ale myślę, że mogę również lub alternatywnie użyć mojej metody, aby zapisać zmienną, gdy się zmienia (np. za pomocą putBoolean)

 22
Author: torwalker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-16 17:11:47

Chociaż przyjęta odpowiedź jest prawidłowa, istnieje szybsza i łatwiejsza metoda zapisywania stanu aktywności na Androidzie za pomocą biblioteki o nazwie Icepick. Icepick jest procesorem adnotacji, który dba o cały kod boilerplate używany do zapisywania i przywracania stanu dla Ciebie.

Robienie czegoś takiego z 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);
  }
}

To to samo co robienie tego:

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 będzie działał z dowolnym obiektem, który zapisuje swój stan za pomocą Bundle.

 21
Author: kevinc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-26 03:07:39

Podczas tworzenia aktywności wywoływana jest metoda onCreate ().

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

SavedInstanceState jest obiektem klasy Bundle, który po raz pierwszy ma wartość null, ale zawiera wartości po odtworzeniu. Aby zapisać stan aktywności, musisz nadpisać metodę onSaveInstanceState ().

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

Umieść swoje wartości w" outState " obiekt Bundle, taki jak outState.putString ("klucz", "Witamy z powrotem") i zapisać dzwoniąc super. Gdy aktywność zostanie zniszczona, jej stan zostanie zapisany w obiekcie Bundle i może być przywrócone po rekreacji w onCreate () lub onRestoreInstanceState (). Pakiet otrzymany w OnCreate() i onRestoreInstanceState () są takie same.

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

Lub

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }
 18
Author: Mansuu....,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-23 10:29:47

Istnieją zasadniczo dwa sposoby wdrożenia tej zmiany.

  1. za pomocą onSaveInstanceState() i onRestoreInstanceState().
  2. w manifeście android:configChanges="orientation|screenSize".
Naprawdę nie polecam stosowania drugiej metody. Ponieważ w jednym z moich doświadczeń powodowało to, że połowa ekranu urządzenia była czarna podczas obracania się z pionowego do poziomego i odwrotnie.

Używając pierwszej metody, o której mowa powyżej, możemy zapisać dane w przypadku zmiany orientacji lub zmiany konfiguracji. Znam sposób, w jaki można przechowywać wszelkie typ danych wewnątrz obiektu savedInstance state.

przykład: rozważ przypadek, jeśli chcesz utrzymać obiekt Json. Utwórz klasę modelu za pomocą getterów i setterów .

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

teraz w swojej aktywności w onCreate i onSaveInstanceState metoda wykonaj następujące czynności. Będzie to wyglądało mniej więcej tak:

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

}
 16
Author: Krishna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-22 05:48:59

Jest to jeden z najbardziej znanych i cenionych na świecie producentów i dystrybutorów w Europie, a zarazem jeden z najbardziej rozpoznawalnych producentów i dystrybutorów w Europie.]}

@VVK-częściowo się nie zgadzam. Niektóre sposoby wychodzenia z aplikacji nie uruchamiają się onSaveInstanceState (oSIS). Ogranicza to przydatność osi. Its warto wspierać, dla minimalnych zasobów systemu operacyjnego, ale jeśli aplikacja chce Przywróć użytkownikowi stan, w którym był, bez względu na to, jak aplikacja na po zakończeniu, konieczne jest użycie metody trwałego przechowywania. używam onCreate, aby sprawdzić pakiet, a jeśli go brakuje, sprawdź trwałe przechowywanie.To centralizuje proces podejmowania decyzji. Mogę odzyskiwanie po awarii, lub przycisk Wstecz exit lub niestandardowy element menu Exit, lub get back to screen user was on many days later. - ToolmakerSteve Sep 19 '15 at 10: 38

 12
Author: samis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-07 16:02:51

Kod Kotlina:

Zapisz:

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

A następnie w onCreate() lub 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

Dodaj wartości domyślne, jeśli nie chcesz mieć opcji

 11
Author: Rafols,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 19:42:32

Aby uzyskać dane stanu aktywności zapisane w onCreate(), najpierw należy zapisać dane w savedInstanceState poprzez nadpisanie metody SaveInstanceState(Bundle savedInstanceState).

Po wywołaniu metody activity destroy SaveInstanceState(Bundle savedInstanceState) i tam zapisujesz dane, które chcesz zapisać. I otrzymujesz to samo w onCreate() po ponownym uruchomieniu aktywności.(savedInstanceState nie będzie null, ponieważ zapisałeś w nim niektóre dane przed zniszczeniem aktywności)

 10
Author: iamabhaykmr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-03 12:02:09

Proste szybkie rozwiązanie tego problemu polega na użyciu IcePick

Najpierw skonfiguruj bibliotekę w app/build.gradle

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

Teraz sprawdźmy poniższy przykład, jak zapisać stan w aktywności

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);
  }
}
Działa dla działań, fragmentów lub dowolnego obiektu, który musi serializować swój stan na pakiecie (np.]}

Icepick może również wygenerować kod stanu instancji dla niestandardowych widoków:

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
}
 7
Author: THANN Phearum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 02:26:57

Nie wiem, czy moje rozwiązanie jest źle widziane, ale używam usługi bound do utrzymywania stanu ViewModel. Niezależnie od tego, czy przechowujesz go w pamięci w usłudze, czy utrzymujesz i pobierasz z bazy danych SQLite, zależy to od twoich wymagań. To właśnie robią usługi o dowolnym smaku, świadczą usługi takie jak utrzymywanie stanu aplikacji i abstrakcyjna wspólna logika biznesowa.

Ze względu na ograniczenia pamięci i przetwarzania nieodłącznie związane z urządzeniami mobilnymi traktuję widoki Androida w podobny sposób jak stronę internetową. Strona nie zachowuje stanu, jest wyłącznie składnikiem warstwy prezentacji, którego jedynym celem jest przedstawienie stanu aplikacji i akceptacja danych wejściowych użytkownika. Najnowsze trendy w architekturze aplikacji internetowych wykorzystują wzorzec model, widok, kontroler (MVC), gdzie strona jest widokiem, dane domeny to model, a kontroler znajduje się za usługą internetową. Ten sam wzór można zastosować w Androidzie, a widok jest, dobrze ... Widok, model to dane twojej domeny, a Kontroler jest realizowany jako usługa związana Android. Gdy chcesz, aby Widok wszedł w interakcję z kontrolerem, połącz go przy starcie/wznowieniu i rozłącz przy zatrzymaniu / wstrzymaniu.

To podejście daje dodatkowy bonus egzekwowania zasady projektowania separacji troski w tym, że wszystkie z was aplikacji logiki biznesowej mogą być przeniesione do usługi, która zmniejsza zduplikowaną logikę w wielu widokach i pozwala widok do egzekwowania innej ważnej zasady projektowania, jednej odpowiedzialności.

 7
Author: ComeIn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-28 03:39:57

Kotlin

Musisz nadpisać onSaveInstanceState i onRestoreInstanceState aby przechowywać i pobierać zmienne, które chcesz być trwałe

Wykres cyklu życia

Przechowuj zmienne

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

Pobierz zmienne

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
}
 6
Author: Sazzad Hissain Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-01 07:05:38

Teraz Android dostarcza ViewModels do zapisywania stanu, powinieneś spróbować użyć tego zamiast saveInstanceState.

 3
Author: M Abdul Sami,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-27 11:51:27

Istnieje sposób, aby Android zapisywał Stany bez implementacji żadnej metody. Po prostu dodaj tę linię do deklaracji manifestu w aktywności:

android:configChanges="orientation|screenSize"

Powinno wyglądać tak:

<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>

Tutaj można znaleźć więcej informacji na temat tej nieruchomości.

Zaleca się, aby Android zajął się tym za ciebie niż ręczną obsługą.

 2
Author: IgniteCoders,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-21 11:32:27

Co oszczędzać, a czego nie?

Zastanawiałeś się kiedyś, dlaczego tekst w {[4] } jest zapisywany automatycznie podczas zmiany orientacji? Ta odpowiedź jest dla Ciebie.

Gdy instancja aktywności zostanie zniszczona ,a System odtworzy nową instancję(na przykład zmiana konfiguracji). Próbuje go odtworzyć używając zestawu zapisanych danych ze starego stanu aktywności (stan instancji ).

Stan instancji jest zbiorem par klucz-wartość przechowywanych w obiekt Bundle.

Domyślnie system zapisuje na przykład obiekty widoku w pakiecie.

  • tekst w EditText
  • pozycja przewijania w ListView, itd.

Jeśli chcesz Zapisać inną zmienną jako część stanu instancji, powinieneś nadpisać onSavedInstanceState(Bundle savedinstaneState) metoda.

Na przykład int currentScore w grze

Więcej informacji na temat onSavedInstanceState (Bundle savedinstate) podczas zapisywania danych
@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);
}

Więc przez pomyłkę, jeśli zapomnisz zadzwonić super.onSaveInstanceState(savedInstanceState);domyślne zachowanie nie będzie działać czyli tekst w EditText nie zostanie zapisany.

Który wybrać do przywrócenia stanu aktywności?

 onCreate(Bundle savedInstanceState)

lub

onRestoreInstanceState(Bundle savedInstanceState)

Obie metody otrzymują ten sam obiekt Bundle, więc nie ma znaczenia, gdzie zapisujesz logikę przywracania. Jedyną różnicą jest to, że w metodzie onCreate(Bundle savedInstanceState) będziesz musiał podać sprawdzanie null, gdy nie jest ono potrzebne w tym drugim przypadku. Inne odpowiedzi mają już urywki kodu. Możesz je skierować.

Więcej szczegółów na temat onRestoreInstanceState (Pakiet 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);
}

Zawsze wywołaj super.onRestoreInstanceState(savedInstanceState);, aby system domyślnie przywracał hierarchię widoku

Bonus

onSaveInstanceState(Bundle savedInstanceState) jest wywoływany przez system tylko wtedy, gdy użytkownik zamierza wrócić do aktywności. Na przykład, używasz aplikacji X i nagle dostajesz połączenie. Przechodzisz do aplikacji dzwoniącej i wracasz do aplikacji X. W tym przypadku onSaveInstanceState(Bundle savedInstanceState) zostanie wywołana metoda.

Ale rozważ to, jeśli użytkownik naciśnie przycisk Wstecz. Zakłada się, że użytkownik nie zamierza wracać do aktywności, stąd w tym przypadku onSaveInstanceState(Bundle savedInstanceState) nie będzie wywoływany przez system. Należy wziąć pod uwagę wszystkie scenariusze podczas zapisywania danych.

Odpowiednie linki:

Demo na domyślnym zachowaniu
oficjalna dokumentacja Androida .

 1
Author: Rohit Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-19 04:41:43

Teraz ma sens zrobić 2 sposoby w modelu widoku. jeśli chcesz zapisać pierwszą instancję jako zapisaną instancję: Możesz dodać parametr state w modelu widoku w następujący sposób https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#java

Lub możesz zapisać zmienne lub obiekt w modelu widoku, w tym przypadku model widoku będzie utrzymywał cykl życia do czasu zniszczenia aktywności.

public class HelloAndroidViewModel extends ViewModel {
   public Booelan firstInit = false;

    public HelloAndroidViewModel() {
        firstInit = false;
    }
    ...
}

public class HelloAndroid extends Activity {

  private TextView mTextView = null;
  HelloAndroidViewModel viewModel = ViewModelProviders.of(this).get(HelloAndroidViewModel.class);
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    //Because even if the state is deleted, the data in the viewmodel will be kept because the activity does not destroy
    if(!viewModel.firstInit){
        viewModel.firstInit = true
        mTextView.setText("Welcome to HelloAndroid!");
    }else{
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}
 1
Author: Umut ADALI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-10 07:45:56

Możesz użyć Live Data i View Model dla L ifecycle Handel z JetPack. zobacz ten odnośnik:

Https://developer.android.com/topic/libraries/architecture/livedata

 0
Author: sana ebadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-20 16:56:04