onSaveInstanceState () i onRestoreInstanceState ()

138

Próbuję zapisać i przywrócić stan pliku Activityprzy użyciu metod onSaveInstanceState()i onRestoreInstanceState().

Problem w tym, że nigdy nie wchodzi do onRestoreInstanceState()metody. Czy ktoś może mi wyjaśnić, dlaczego tak jest?

BlaBRA
źródło
1
@Nitin: dzięki za udostępnienie linku ... to wyjaśniło mi kilka rzeczy +1
Taliadon
2
@NitinBansal link nie działa.
ashishdhiman2007

Odpowiedzi:

191

Zwykle przywracasz swój stan w onCreate(). Można go onRestoreInstanceState()również przywrócić , ale niezbyt często. ( onRestoreInstanceState()nazywa się po onStart(), a onCreate()nazywa się przed onStart().

Użyj metod put do przechowywania wartości w onSaveInstanceState():

protected void onSaveInstanceState(Bundle icicle) {
  super.onSaveInstanceState(icicle);
  icicle.putLong("param", value);
}

I przywróć wartości w onCreate():

public void onCreate(Bundle icicle) {
  if (icicle != null){
    value = icicle.getLong("param");
  }
}
Robert
źródło
2
problem polega na tym, że używam startActivity, aby powrócić do czynności A. Wracając do czynności B, obiektem jest zerowy sopel lodu.
BlaBRA
5
Jeśli dobrze rozumiem, oto co robisz: Z B wywołujesz startActivity (A). Następnie z A wywołujesz finish (), aby wrócić do B. Zgadza się? W takim przypadku Twoje pierwsze działanie B nie zostanie zniszczone i nie zostanie wywołane ani onCreate (), ani onRestoreInstanceState (). Metody te są wywoływane tylko wtedy, gdy są potrzebne, to znaczy gdy działanie zostało zniszczone i musi zostać odtworzone przez system.
Robert
4
Powinienem dodać, że twoja pierwsza aktywność, B, może zostać zniszczona z powodu słabej pamięci. Spowoduje to wyzwolenie onCreate i onRestoreInstanceState.
Robert
1
erikb, tak, czynność B zostanie wznowiona lub w przypadku, gdy system operacyjny odzyska ją, odtworzy, a następnie wznowi.
Robert,
1
Och, właśnie dlatego
Al Lelopath
149

onRestoreInstanceState()jest wywoływana tylko podczas odtwarzania aktywności po jej zabiciu przez system operacyjny. Taka sytuacja ma miejsce, gdy:

  • orientacja urządzenia ulegnie zmianie (Twoja aktywność zostanie zniszczona i odtworzona).
  • jest inna aktywność przed twoją i w pewnym momencie system operacyjny zabija twoją aktywność, aby zwolnić pamięć (na przykład). Następnym razem, gdy zaczniesz swoją aktywność, zostanie wywołana metoda onRestoreInstanceState ().

W przeciwieństwie do tego: jeśli jesteś w swojej aktywności i naciśniesz Backprzycisk na urządzeniu, Twoja aktywność zostanie zakończona () ed (tj. Pomyśl o tym jak o wyjściu z aplikacji komputerowej) i następnym razem, gdy uruchomisz aplikację, zostanie uruchomiona „od nowa”, tj. Bez zapisany stan, ponieważ celowo opuściłeś go po uderzeniu Back.

Innym źródłem nieporozumień jest to, że gdy aplikacja traci fokus na inną aplikację, onSaveInstanceState()jest wywoływana, ale po powrocie do aplikacji onRestoreInstanceState()może nie zostać wywołana. Tak jest w przypadku opisanym w pierwotnym pytaniu, tj. Jeśli twoja czynność NIE została zabita w okresie, gdy inna czynność była z przodu onRestoreInstanceState(), NIE zostanie wywołana, ponieważ twoja aktywność jest prawie „żywa”.

Podsumowując, jak podano w dokumentacji onRestoreInstanceState():

Większość implementacji użyje po prostu onCreate (Bundle) do przywrócenia ich stanu, ale czasami wygodnie jest to zrobić tutaj po wykonaniu całej inicjalizacji lub pozwolić podklasom zdecydować, czy użyć domyślnej implementacji. Domyślna implementacja tej metody wykonuje przywracanie dowolnego stanu widoku, który został wcześniej zamrożony przez onSaveInstanceState (Bundle).

Tak jak to przeczytałem: nie ma powodu do nadpisywania, onRestoreInstanceState()chyba że tworzysz podklasę Activityi oczekuje się, że ktoś podklasuje twoją podklasę.

Ognyan
źródło
3
tak, to wydaje się być słuszne, ale jest do bani. imo należy go również uruchomić w przypadku powrotu do działania z innego działania. jest wiele sytuacji, w których tego potrzebujesz.
masi
4
@masi istnieją już inne metody wywoływane w działaniu, gdy użytkownik do niego powraca (z innego działania). Metoda onSave / RestoreInstanceState () jest używana do innego określonego celu, to wszystko.
superjos
8

Stan, w którym zapisujesz, onSaveInstanceState()jest później dostępny podczas onCreate()wywołania metody. Dlatego użyj onCreate(i jego Bundleparametru), aby przywrócić stan swojej aktywności.

Konstantin Burov
źródło
4

Aby obejść ten problem, możesz przechowywać pakiet z danymi, które chcesz zachować, w zamiarze, którego używasz do rozpoczęcia działania A.

Intent intent = new Intent(this, ActivityA.class);
intent.putExtra("bundle", theBundledData);
startActivity(intent);

Działanie A musiałoby przekazać to z powrotem do działania B. Możesz pobrać intencję w metodzie onCreate działania B.

Intent intent = getIntent();
Bundle intentBundle;
if (intent != null)
    intentBundle = intent.getBundleExtra("bundle");
// Do something with the data.

Innym pomysłem jest utworzenie klasy repozytorium do przechowywania stanu aktywności i umieszczenie każdego z twoich działań w odniesieniu do tej klasy (możliwe przy użyciu struktury singleton). Jednak zrobienie tego prawdopodobnie jest bardziej kłopotliwe niż warte.

sotrh
źródło
3

Najważniejsze jest to, że jeśli nie przechowywać w onSaveInstanceState()czym onRestoreInstanceState()nie zostanie wywołana. To jest główna różnica między restoreInstanceState()i onCreate(). Upewnij się, że naprawdę coś przechowujesz. Najprawdopodobniej to twój problem.

user1771286
źródło
1
Zostanie wywołana onRestoreInstanceState (), nawet jeśli nie przechowujesz niczego w OnSaveInstanceState ()
abh22ishek
3

Zauważyłem, że onSaveInstanceState jest zawsze wywoływana, gdy na pierwszym planie pojawia się inne działanie. I tak jest onStop.

Jednak onRestoreInstanceState została wywołana tylko wtedy, gdy wywołano również onCreate i onStart. A onCreate i onStart NIE zawsze były wywoływane.

Wygląda więc na to, że Android nie zawsze usuwa informacje o stanie, nawet jeśli działanie jest przenoszone w tle. Jednak wywołuje metody cyklu życia, aby zapisać stan tylko dla bezpieczeństwa. Dlatego jeśli stan nie zostanie usunięty, system Android nie wywoła metod cyklu życia w celu przywrócenia stanu, ponieważ nie są one potrzebne.

Rysunek 2 to opisuje.

brzęczeć
źródło
2

Myślę, że ten wątek był dość stary. Wspomnę tylko o innym przypadku, który onSaveInstanceState()również zostanie wywołany, to kiedy zadzwonisz Activity.moveTaskToBack(boolean nonRootActivity).

macio.Jun
źródło
1

Jeśli obsługujesz zmiany orientacji działania za pomocą android:configChanges="orientation|screenSize"i onConfigurationChanged(Configuration newConfig), onRestoreInstanceState()nie zostanie wywołany.

Rajkiran
źródło
1

Nie jest konieczne, aby onRestoreInstanceState zawsze był wywoływany po onSaveInstanceState.

Zwróć uwagę, że: onRestoreInstanceState będzie zawsze wywoływane, gdy aktywność zostanie obrócona (gdy orientacja nie jest obsługiwana) lub otworzy Twoją aktywność, a następnie otwórz inne aplikacje, aby instancja aktywności została wyczyszczona z pamięci przez system operacyjny.

Ashutosh Srivastava
źródło
1

Z dokumentacji Przywróć stan interfejsu użytkownika aktywności przy użyciu zapisanego stanu instancji jest to:

Zamiast przywracać stan podczas onCreate (), możesz wybrać implementację onRestoreInstanceState (), którą system wywołuje po metodzie onStart (). System wywołuje metodę onRestoreInstanceState () tylko wtedy, gdy istnieje zapisany stan do przywrócenia, więc nie ma potrzeby sprawdzania, czy pakiet jest pusty :

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

IMO, to jest bardziej przejrzysty sposób niż sprawdzanie tego w onCreate i lepiej pasuje do zasady pojedynczej odpowiedzialności.

Teoman shipahi
źródło
0

W moim przypadku onRestoreInstanceStatezostał wywołany, gdy czynność została zrekonstruowana po zmianie orientacji urządzenia. onCreate(Bundle)został wywołany jako pierwszy, ale pakiet nie miał klucza / wartości, który ustawiłemonSaveInstanceState(Bundle) .

Zaraz potem onRestoreInstanceState(Bundle)został wywołany z pakietem, który miał poprawne klucze / wartości.

elvitucho
źródło
0

Właśnie wpadłem na to i zauważyłem, że dokumentacja zawiera moją odpowiedź:

„Ta funkcja nigdy nie zostanie wywołana ze stanem zerowym”.

https://developer.android.com/reference/android/view/View.html#onRestoreInstanceState(android.os.Parcelable)

W moim przypadku zastanawiałem się, dlaczego onRestoreInstanceState nie jest wywoływany podczas początkowej instancji. Oznacza to również, że jeśli nic nie zapiszesz, nie zostanie to wywołane, gdy przejdziesz do rekonstrukcji widoku.

Ben Scannell
źródło
0

Mogę to zrobić (przepraszam, że to nie java, ale to nie jest problem ...):

private int iValue = 1234567890;

function void MyTest()
{
    Intent oIntent = new Intent (this, typeof(Camera2Activity));
    Bundle oBundle = new Bundle();
    oBundle.PutInt("MYVALUE", iValue); //=> 1234567890
    oIntent.PutExtras (oBundle);
    iRequestCode = 1111;
    StartActivityForResult (oIntent, 1111);
}

I W TWOJEJ DZIAŁALNOŚCI DLA REZULTATU

private int iValue = 0;

protected override void OnCreate(Bundle bundle)
{
    Bundle oBundle =  Intent.Extras;
    if (oBundle != null)
    {
        iValue = oBundle.GetInt("MYVALUE", 0);
        //=>1234567890
    }
}

private void FinishActivity(bool bResult)
{
    Intent oIntent = new Intent();
    Bundle oBundle = new Bundle();
    oBundle.PutInt("MYVALUE", iValue);//=>1234567890
    oIntent.PutExtras(oBundle);
    if (bResult)
        {
            SetResult (Result.Ok, oIntent);
        }
    else
        SetResult(Result.Canceled, oIntent);
    GC.Collect();
    Finish();
}

WRESZCIE

protected override void OnActivityResult(int iRequestCode, Android.App.Result oResultCode, Intent oIntent)
{
    base.OnActivityResult (iRequestCode, oResultCode, oIntent);
    iValue = oIntent.Extras.GetInt("MYVALUE", -1); //=> 1234567890
}
EDynamic90
źródło