Pytanie Zapisywanie stanu aktywności w Androidzie przy użyciu stanu zapisania wystąpienia


Pracowałem nad platformą Android SDK i nie jest jasne, jak zapisać stan aplikacji. Biorąc pod uwagę tę niewielką zmianę w przykładzie "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 to wystarczy w najprostszym przypadku, ale zawsze odpowiada pierwszą wiadomością, bez względu na to, jak oddalam się od aplikacji.

Jestem pewien, że rozwiązanie jest tak proste jak przesłonięcie onPause lub coś w tym stylu, ale odkładam dokumentację na około 30 minut i nie znalazłem niczego oczywistego.


2238
2017-09-30 04:41


pochodzenie


Kiedy jest zapisana wartośćInstanceState == null i kiedy nie jest pusta? - Trojan.ZBOT
Natychmiastowo niszczysz swoją aktywność przez - jak powiedziałeś - poruszając się od niej, na przykład przez cofnięcie. W rzeczywistości scenariusz, w którym używane jest to "savedInstanceState", ma miejsce, gdy Android niszczy twoją aktywność rekreacyjną. W intencji: Jeśli zmienisz język telefonu podczas działania, musisz załadować inne zasoby z twojego projektu. Innym bardzo częstym scenariuszem jest obrócenie telefonu na bok, aby odtworzyć aktywność i wyświetlić ją w orientacji poziomej. - villoren
Aby uzyskać drugą wiadomość, włącz opcję "Nie zatrzymuj działań" w opcjach dewelopera. Naciśnij przycisk home i wróć z poprzedniego. - Yaroslav Mytkalyk
to jest bardzo pomocne developer.android.com/training/basics/activity-lifecycle/... - Syed Raza Mehdi
możesz to zrobić za pomocą: onSaveInstanceState (Bundle savedInstanceState) - HPbyP


Odpowiedzi:


Musisz przesłonić onSaveInstanceState(Bundle savedInstanceState) i wpisz wartości stanu aplikacji, które chcesz zmienić na Bundle parametr podobny do tego:

@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 ("Nazwa-Wartość-Nazwa") i zostanie przekazany do onCreate() i również onRestoreInstanceState() gdzie można wyodrębnić takie wartości:

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

Zwykle używa się tej techniki do przechowywania wartości instancji dla aplikacji (wybory, niezapisany tekst itp.).


2275
2017-09-30 06:12



Czy jest szansa, że ​​działa to na telefonie, ale nie na emulatorze? Nie mogę uzyskać niezerowej wartości zapisanego stanuInstanceState. - Adam Jack
Mam ArrayList punktów, jak zapisać wszystkie punkty na liście, a następnie je przywrócić? - AZ_
OSTROŻNIE: zanim dodasz swoje wartości do Pakietu, musisz zadzwonić do super.onSaveInstanceState (savedInstanceState), bo inaczej zostanie usunięty z tego połączenia (Droid X Android 2.2). - jkschneider
Ostrożnie: oficjalna dokumentacja stwierdza, że ​​należy zapisać ważne informacje w ramach metody onPause, ponieważ metoda onsaveinstance nie jest częścią cyklu życia systemu Android. developer.android.com/reference/android/app/Activity.html - schlingel
Fakt ten skutecznie sprawia onSaveInstanceState prawie bezużyteczne, z wyjątkiem samego przypadku zmiany orientacji ekranu. W prawie wszystkich innych przypadkach nigdy nie można na nim polegać i trzeba ręcznie zapisać stan interfejsu użytkownika w innym miejscu. Możesz też zapobiec zabijaniu aplikacji przez zastąpienie zachowania przycisku BACK. Nie rozumiem, dlaczego oni w ogóle wdrożyli to w ten sposób. Całkowicie niezrozumiałe. I nie możesz mieć tego Pakunku, który system daje ci do zapisania rzeczy, z wyjątkiem tej bardzo szczególnej metody. - chakrit


The savedInstanceState służy tylko do zapisywania stanu powiązanego z bieżącą instancją działania, na przykład bieżących informacji nawigacji lub selekcji, więc jeśli Android niszczy i odtwarza działanie, może wrócić tak, jak było wcześniej. Zobacz dokumentację dla onCreate i onSaveInstanceState

Aby uzyskać bardziej długowieczny stan, należy rozważyć użycie bazy danych SQLite, pliku lub preferencji. Widzieć Oszczędzanie trwałego państwa.


375
2017-09-30 05:03



Kiedy jest zapisana wartośćInstanceState == null i kiedy nie jest pusta? - Trojan.ZBOT
savedInstanceState ma wartość NULL, gdy system tworzy nowe wystąpienie aktywności, a nie wartość NULL, gdy jest przywracana. - Gabriel Câmara
... co rodzi pytanie gdy czy system musi utworzyć nową instancję działania. Niektóre sposoby wyjścia z aplikacji nie tworzą pakietu, więc trzeba utworzyć nową instancję. To jest podstawowy problem; to znaczy, że nie można polegać o istnieniu pakietu i musi zrobić alternatywne środki trwałego przechowywania. Zaletą metody onSave / onRestoreInstanceState jest to, że jest to mechanizm, który może wykonać system nagle, bez zużywania znacznych zasobów systemowych. Więc dobrze jest to wspierać, a także mieć trwałe miejsce do przechowywania, aby uzyskać lepsze wyjście z aplikacji. - ToolmakerSteve


Zauważ, że tak NIE bezpieczny w użyciu onSaveInstanceState i onRestoreInstanceState  dla trwałych danych, zgodnie z dokumentacją dotyczącą stanów aktywności w http://developer.android.com/reference/android/app/Activity.html.

Dokument stwierdza (w sekcji "Activity Lifecycle"):

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

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

EDYTOWAĆ: Aby uzyskać dalsze wyjaśnienia, oto: onSaveInstanceState() dokumentacja:

Ta metoda jest wywoływana, zanim aktywność może zostać zabita, tak aby po jej uruchomieniu   wraca jakiś czas w przyszłości, może przywrócić swój stan. Dla   na przykład, jeśli aktywność B jest uruchamiana przed działaniem A i w niektórych   Akcja punktu A jest zabijana, aby odzyskać zasoby, działalność A będzie miała   szansa na zapisanie bieżącego stanu interfejsu użytkownika przez to   metoda, aby po powrocie użytkownika do działania A, stan   interfejs użytkownika można przywrócić za pomocą onCreate(Bundle) lub    onRestoreInstanceState(Bundle).


366
2018-05-25 23:22



Po prostu do nitpick: to też nie jest niebezpieczne. To zależy od tego, co chcesz zachować i jak długo, którego @Bernard nie jest całkiem jasne w swoim oryginalnym pytaniu. InstanceState jest idealny do zachowania bieżącego stanu interfejsu użytkownika (dane wprowadzane do elementów sterujących, bieżących pozycji na listach itd.), Natomiast tryb Pause / Resume to jedyna możliwość długoterminowego przechowywania trwałego. - Pontus Gagge
To powinno być obniżone. Nie jest bezpiecznie używać na (Save | Restore) InstanceState, takich jak metody cyklu życia (to znaczy robić cokolwiek innego niż zapisywać / przywracać stan). Doskonale nadają się do zapisywania / przywracania stanu. Ponadto, w jaki sposób chcesz zapisać / przywrócić stan w trybie onPause i onResume? Nie dostaniesz Pakieli w tych metodach, z których możesz korzystać, więc będziesz musiał użyć innego oszczędzania stanu w bazach danych, plikach itp., Co jest głupie. - Felix
Nie powinniśmy głosować na tę osobę, przynajmniej starał się przejść przez tę dokumentację i myślę, że jesteśmy tutaj po to, aby rzeczywiście budować kompetentną społeczność i pomagać sobie nawzajem, aby NIE GŁOSOWAĆ. więc 1 głos do góry za wysiłek, a ja poproszę was, abyście nie głosowali, raczej głosowali w górę lub nie głosowali ... ta osoba oczyściła zamieszanie, które ktoś chciałby mieć podczas przeglądania dokumentacji. 1 głos w górę :) - AZ_
Nie sądzę, żeby ta odpowiedź zasługiwała na pochwałę. W końcu próbował odpowiedzieć i zacytował fragment z doco. - GSree
Ta odpowiedź jest absolutnie poprawna i zasługuje na głos UP, a nie na dół! Pozwól mi wyjaśnić różnicę między stanami dla tych, którzy jej nie widzą. Stan GUI, podobnie jak wybrane przyciski radiowe i trochę tekstu w polu wejściowym, jest znacznie mniej ważny niż stan danych, jak zapisy dodane do listy wyświetlanej w ListView. Te ostatnie muszą być przechowywane w bazie danych w trybie onPause, ponieważ jest to jedyne gwarantowane połączenie. Jeśli umieścisz go w opcji SaveSstanceState, ryzykujesz utratę danych, jeśli nie zostanie to wywołane. Ale jeśli wybór przycisku radiowego nie zostanie zapisany z tego samego powodu - nie jest to wielka sprawa. - JBM


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, sposobu przechowywania informacji o stanie i zapisywania do stanu Bundle i SharedPreferences i spójrz tutaj.

Artykuł obejmuje trzy podejścia:

Przechowuj lokalne dane sterujące zmiennymi / interfejsami użytkownika dotyczące czasu życia aplikacji (np. Tymczasowo) za pomocą pakietu Stan 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);
}

Przechowuj lokalne dane sterujące zmiennymi / interfejsem użytkownika między instancjami aplikacji (np. Na stałe) przy użyciu preferencji udostępnionych

[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 instancji obiektów w pamięci pomiędzy kolejnymi aktywnościami w obrębie aplikacji za pomocą Instancji bez konfiguracji

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

171
2017-08-27 13:54



@ MartinBelcher-Eigo Artykuł mówi o danych w SharedPreferences, że "Te dane są zapisywane w bazie danych na urządzeniu .." Uważam, że dane są przechowywane w pliku w katalogu aplikacji systemu plików. - Tom
@Tom Dane SharefPrefs są zapisywane do pliku xml. Czy xml jest rodzajem bazy danych? Powiedziałbym, że to;) - MaciejGórski
Zauważ, że editor.apply() jest szybszy niż editor.commit(). - Fred
Link nie działa. - Sakiboy


To jest klasyczna "gotcha" rozwoju Androida. Są tu dwa problemy:

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

Przeglądając wszystkie wątki, podejrzewam, że twórcy często mówią o tych dwóch różnych sprawach jednocześnie ... stąd całe zamieszanie i raporty "to nie działa dla mnie".

Po pierwsze, aby wyjaśnić "zamierzone" zachowanie: onSaveInstance i onRestoreInstance są delikatne i tylko dla stanu przejściowego. Przeznaczeniem (afaict) jest obsługa Odtwarzania aktywności po obróceniu telefonu (zmiana orientacji). Innymi słowy, zamierzonym użyciem jest, gdy twoja Aktywność jest nadal logicznie "na wierzchu", ale wciąż musi zostać przywrócona przez system. Zapisany pakiet nie jest przechowywany poza procesem / pamięcią / gc, więc nie można na nim polegać, jeśli twoja działalność zostanie przeniesiona na drugi plan. Tak, być może pamięć Twojej aktywności przetrwa podróż do tła i ucieknie przed GC, ale nie jest to wiarygodne (ani nie jest przewidywalne).

Więc jeśli masz scenariusz, w którym jest znaczący "postęp użytkownika" lub stan, który powinien być utrzymany pomiędzy "uruchomieniami" twojej aplikacji, poradnikiem jest użycie onPause i onResume. Musisz samodzielnie wybrać i przygotować własny sklep.

ALE - istnieje 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 twoja aplikacja zostanie uruchomiona z flagą pojedynczego paska zadań, a następnie uruchomisz ją z poziomu ekranu głównego lub menu uruchamiania, ta kolejna inwokacja stworzy nowe zadanie ... w efekcie będziesz mieć dwa różne wystąpienia aplikacji zamieszkujących ten sam stos ... który bardzo szybko staje się bardzo dziwny. Wydaje się, że dzieje się to podczas uruchamiania aplikacji podczas jej tworzenia (np. Z Eclipse lub Intellij), dlatego programiści często w nią uczestniczą. Ale także dzięki niektórym mechanizmom aktualizacji w sklepie z aplikacjami (co wpływa również na użytkowników).

Walczyłem przez te wątki przez kilka godzin, zanim zdałem sobie sprawę, że moim głównym problemem był ten błąd, a nie zamierzone zachowanie w strukturze. Świetny napis i obejście (UPDATE: patrz poniżej) wydaje się pochodzić od użytkownika @kaciula w tej odpowiedzi:

Zachowanie prasy klawiszowej w domu

ZAKTUALIZUJ Czerwiec 2013: Kilka miesięcy później w końcu znalazłem "poprawne" rozwiązanie. Nie musisz samodzielnie zarządzać flagami stateful startedApp, możesz to wykryć z frameworka i odpowiednio usunąć kaucję. Używam tego na początku mojego 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;
    }
}

126
2017-10-19 23:47



Dlaczego Android ponownie stał się mobilnym systemem operacyjnym drugiego wyboru? zakopuje głowę w dłoniach - Nick Bauer


onSaveInstanceStatejest wywoływana, gdy system potrzebuje pamięci i zabija aplikację. Nie jest wywoływana, gdy użytkownik zamyka aplikację. Myślę więc, że stan aplikacji również powinien zostać zapisany onPause Powinien być zapisany w pamięci trwałej, takiej jak Preferences lub Sqlite


70
2018-05-07 00:21



Przepraszamy, to nie jest poprawne. Parametr onSaveInstanceState zostaje wywołany przed ponownym utworzeniem działania. tj. za każdym razem, gdy użytkownik obraca urządzenie. Jest przeznaczony do przechowywania stanów przejściowych widoku. Gdy Android zmusza aplikację do zamknięcia, funkcja onSaveInstanceState NIE jest wywoływana (dlatego przechowywanie ważnych danych aplikacji jest niebezpieczne). onPause ma jednak gwarancję, że zostanie wywołane przed zabiciem aktywności, więc powinno być używane do przechowywania stałych informacji w preferencjach lub Squlite. Właściwa odpowiedź, złe powody. - moveaway00
@ moveaway00 - częściowo niepoprawny. Jeśli system zabija aplikację, aby odzyskać zasoby, to robi wywołać onSaveInstanceState. Od developer.android.com/training/basics/activity-lifecycle/... - "System może również zniszczyć twoją aktywność ... aktywność na pierwszym planie wymaga więcej zasobów, więc system musi zamknąć procesy w tle, aby odzyskać pamięć. ... jeśli system niszczy działanie z powodu ograniczenia systemowe ... system pamięta, że ​​istniał ... Zapisane dane ... to zbiór par klucz-wartość przechowywanych w obiekcie Bundle. ". - ToolmakerSteve


Obie metody są użyteczne i ważne, a obie metody najlepiej pasują do różnych scenariuszy:

  1. Użytkownik zamyka aplikację i ponownie otwiera ją w późniejszym terminie, ale aplikacja musi ponownie załadować dane z ostatniej sesji - wymaga to stałego podejścia do przechowywania danych, takiego jak użycie SQLite.
  2. Użytkownik przełącza aplikację, a następnie wraca do oryginału i chce odebrać miejsce, w którym zostało przerwane - zapisz i przywróć dane pakietu (takie jak dane o stanie aplikacji) onSaveInstanceState() i onRestoreInstanceState() jest zwykle wystarczające.

Jeśli zapiszesz dane stanu w sposób ciągły, można go ponownie załadować w onResume() lub onCreate() (lub faktycznie w dowolnym wywołaniu cyklu życia). To może lub nie być pożądane zachowanie. Jeśli przechowujesz go w pakiecie w InstanceState, jest przejściowy i nadaje się tylko do przechowywania danych do użycia w tej samej sesji użytkownika (luźno używam terminu sesja), ale nie pomiędzy "sesjami".

Nie jest tak, że jedno podejście jest lepsze od drugiego, tak jak wszystko, ważne jest, aby zrozumieć, jakie zachowanie jest potrzebne i wybrać najbardziej odpowiednie podejście.


59
2018-06-27 16:17



Jak wybieramy SQLite i Preferencje podczas szukania trwałego magazynu? - Deco