Pracuję na platformie Android SDK i jest trochę niejasne, jak zapisać stan aplikacji. Biorąc pod uwagę tę drobną przeróbkę 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);
}
}
Pomyślałem, że to wystarczy w najprostszym przypadku, ale zawsze reaguje na pierwszą wiadomość, bez względu na to, jak opuściłem aplikację.
Jestem pewien, że rozwiązanie jest tak proste, jak zastąpienie onPause
lub coś w tym rodzaju, ale od 30 minut zajmuję się dokumentacją i nie znalazłem niczego oczywistego.
Odpowiedzi:
Musisz zastąpić
onSaveInstanceState(Bundle savedInstanceState)
i zapisać wartości stanu aplikacji, które chcesz zmienić, doBundle
parametru w następujący sposób:Pakiet jest zasadniczo sposobem przechowywania mapy NVP („para nazwa-wartość”) i zostanie przekazany do,
onCreate()
a takżeonRestoreInstanceState()
tam, gdzie następnie wyodrębnisz wartości z działania w następujący sposób:Lub z fragmentu.
Zazwyczaj używasz tej techniki do przechowywania wartości instancji dla aplikacji (selekcje, niezapisany tekst itp.).
źródło
onSaveInstanceState
prawie bezużytecznym, z wyjątkiem przypadku zmiany orientacji ekranu. W prawie wszystkich innych przypadkach nigdy nie można na nim polegać i konieczne będzie ręczne zapisanie stanu interfejsu użytkownika w innym miejscu. Lub zapobieganie zabiciu aplikacji przez zastąpienie zachowania przycisku WSTECZ. Nie rozumiem, dlaczego w ogóle tak to zaimplementowali. Całkowicie nieintuicyjny. I nie możesz mieć tego Pakietu, w który system da ci zapisywanie rzeczy, z wyjątkiem tej bardzo szczególnej metody.View
s, którym przypisano identyfikatory . ZonSaveInstanceState
dokumentacji: „Domyślna implementacja zajmuje się większością stanu interfejsu użytkownika na instancję, wywołująconSaveInstanceState()
każdy widok w hierarchii, który ma identyfikator, i zapisując identyfikator aktualnie aktywnego widoku (wszystkie są przywracane przez domyślną implementacjęonRestoreInstanceState(Bundle)
) ”Służy
savedInstanceState
tylko do zapisywania stanu związanego z bieżącym wystąpieniem działania, na przykład bieżącej nawigacji lub informacji o zaznaczeniu, dzięki czemu jeśli Android zniszczy i odtworzy działanie, może wrócić tak, jak było wcześniej. Zobacz dokumentację dlaonCreate
ionSaveInstanceState
Aby uzyskać bardziej długotrwały stan, rozważ użycie bazy danych SQLite, pliku lub preferencji. Zobacz Zapisywanie trwałego stanu .
źródło
Należy pamiętać, że NIE jest bezpieczny w użyciu
onSaveInstanceState
ionRestoreInstanceState
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 „Cykl życia aktywności”):
Innymi słowy, umieść kod zapisywania / przywracania trwałych danych w
onPause()
ionResume()
!EDYCJA : Dla dalszego wyjaśnienia, oto
onSaveInstanceState()
dokumentacja:źródło
Mój kolega napisał artykuł wyjaśniający stan aplikacji na urządzeniach z Androidem, w tym wyjaśnień dotyczących działalności cyklu i informacji państwowego, sposobu przechowywania informacji o stanie i zapisywanie do stanu
Bundle
aSharedPreferences
i zapoznać się tutaj .Artykuł obejmuje trzy podejścia:
Przechowuj zmienne lokalne / dane sterujące interfejsu użytkownika przez cały okres użytkowania aplikacji (tj. Tymczasowo) za pomocą pakietu stanu instancji
Przechowuj dane sterujące zmiennej lokalnej / interfejsu użytkownika między instancjami aplikacji (tj. Na stałe) przy użyciu wspólnych preferencji
Utrzymywanie żywotności instancji obiektów w pamięci między działaniami w okresie istnienia aplikacji za pomocą zachowanej instancji innej niż konfiguracja
źródło
Jest to klasyczna „gotcha” rozwoju Androida. Istnieją tutaj dwa problemy:
Przeglądając wszystkie te wątki, podejrzewam, że przez większość czasu programiści rozmawiają o tych dwóch różnych problemach 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 w stanie przejściowym. Zamierzonym zastosowaniem (afaict) jest obsługa odtwarzania aktywności po obróceniu telefonu (zmiana orientacji). Innymi słowy, zamierzone użycie ma miejsce, gdy działanie jest nadal logicznie „na górze”, ale system musi je ponownie przywrócić. Zapisany pakiet nie jest utrwalany poza procesem / pamięcią / gc, więc nie możesz tak naprawdę polegać na tym, jeśli twoja aktywność przechodzi w tło. Tak, być może pamięć Twojej Aktywności przetrwa podróż do tła i ucieknie z GC, ale nie jest to wiarygodne (ani nie jest przewidywalne).
Więc jeśli masz scenariusz, w którym występuje znaczący „postęp użytkownika” lub stan, który powinien zostać utrwalony między „uruchomieniami” aplikacji, wskazówkami jest użycie 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 aplikacja zostanie uruchomiona z flagą SingleTask, a następnie uruchomisz ją z ekranu głównego lub menu uruchamiania, to kolejne wywołanie utworzy NOWE zadanie ... będziesz mieć dwa różne wystąpienia aplikacji zamieszkujących ten sam stos ... co bardzo szybko staje się dziwne. Wydaje się, że dzieje się tak, gdy uruchamiasz aplikację podczas programowania (np. Z Eclipse lub Intellij), więc programiści często się w to angażują. Ale także przez niektóre mechanizmy aktualizacji sklepu z aplikacjami (więc wpływa to również na twoich użytkowników).
Walczyłem przez te wątki przez wiele godzin, zanim zdałem sobie sprawę, że moim głównym problemem był ten błąd, a nie zamierzone zachowanie frameworka. Świetny napis i
obejście(AKTUALIZACJA: patrz poniżej) wydaje się pochodzić od użytkownika @kaciula w tej odpowiedzi:Zachowanie naciśnięcia klawisza Home
AKTUALIZACJA Czerwiec 2013 : Miesiące później w końcu znalazłem „prawidłowe” rozwiązanie. Nie musisz samodzielnie zarządzać stanowymi uruchomionymi flagami aplikacji, możesz to wykryć z frameworka i odpowiednio zwolnić za kaucją. Używam tego na początku mojej LauncherActivity.onCreate:
źródło
onSaveInstanceState
jest wywoływany, gdy system potrzebuje pamięci i zabija aplikację. Nie jest wywoływane, gdy użytkownik zamyka aplikację. Więc myślę, że stan aplikacji powinien być również zapisany w.onPause
Powinien zostać zapisany w pamięci trwałej, takiej jakPreferences
lubSqlite
źródło
Obie metody są przydatne i poprawne, a obie najlepiej nadają się do różnych scenariuszy:
onSaveInstanceState()
ionRestoreInstanceState()
zwykle jest odpowiedni.Jeśli dane stanu zostaną zapisane w sposób trwały, można je ponownie załadować w trybie
onResume()
lubonCreate()
(lub w dowolnym wywołaniu cyklu życia). To może być pożądane zachowanie. Jeśli przechowujesz go w pakiecie wInstanceState
, wówczas jest przejściowy i nadaje się tylko do przechowywania danych do użycia w tej samej „sesji” użytkownika (używam terminu sesja luźno), ale nie między „sesjami”.Nie chodzi o to, że jedno podejście jest lepsze od drugiego, podobnie jak wszystko, ważne jest, aby zrozumieć, jakiego zachowania potrzebujesz i wybrać najbardziej odpowiednie podejście.
źródło
Moim zdaniem stan oszczędzania jest co najwyżej kludge. Jeśli chcesz zapisać trwałe dane, po prostu skorzystaj z bazy danych SQLite . Android ułatwia SOOO .
Coś takiego:
Po tym proste połączenie
źródło
Myślę, że znalazłem odpowiedź. Pozwól mi powiedzieć, co zrobiłem prostymi słowami:
Załóżmy, że mam dwie aktywności, aktywność 1 i aktywność 2 i przechodzę od aktywności 1 do aktywności 2 (wykonałem kilka prac w aktywności 2) i ponownie z powrotem do aktywności 1, klikając przycisk w aktywności 1. Teraz na tym etapie chciałem wrócić do aktywności 2 i chcę zobaczyć moją aktywność 2 w tym samym stanie, kiedy po raz ostatni opuściłem aktywność 2.
W powyższym scenariuszu zrobiłem to, że w manifeście wprowadziłem kilka takich zmian:
I w działaniu 1 na zdarzeniu kliknięcia przycisku zrobiłem tak:
I w action2 na zdarzeniu kliknięcia przycisku zrobiłem tak:
Teraz stanie się to, że cokolwiek zmiany, które wprowadziliśmy w działaniu 2, nie zostaną utracone, i możemy oglądać działanie 2 w takim samym stanie, jak poprzednio.
Wierzę, że to jest odpowiedź i działa mi dobrze. Popraw mnie, jeśli się mylę.
źródło
onSaveInstanceState()
dla danych przejściowych (przywrócone wonCreate()
/onRestoreInstanceState()
),onPause()
dla trwałych danych (przywrócone wonResume()
). Z zasobów technicznych Androida:źródło
Naprawdę
onSaveInstanceState()
jest wywoływany, gdy działanie przechodzi do tła.Cytat z dokumentów: „Ta metoda jest wywoływana, zanim dana czynność może zostać zabita, aby po powrocie w przyszłości mogła przywrócić jej stan”. Źródło
źródło
Aby zredukować płytę kotłową, korzystam z poniższych
interface
iclass
do odczytu / zapisu wBundle
celu zapisania stanu instancji.Najpierw utwórz interfejs, który będzie używany do opisywania zmiennych instancji:
Następnie utwórz klasę, w której odbicie będzie używane do zapisywania wartości w pakiecie:
Przykładowe użycie:
Uwaga: ten kod został zaadaptowany z projektu biblioteki o nazwie AndroidAutowire, który jest licencjonowany na licencji MIT .
źródło
Tymczasem generalnie już nie używam
Cykl życia jest w przypadku większości czynności zbyt skomplikowany i niepotrzebny.
I sam Google stwierdza, że NIE jest nawet wiarygodny.
Moim sposobem jest natychmiastowe zapisanie zmian w preferencjach:
W pewien sposób SharedPreferencje działają podobnie jak pakiety. Naturalnie i na początku takie wartości należy odczytać z preferencji.
W przypadku złożonych danych możesz użyć SQLite zamiast preferencji.
Podczas stosowania tej koncepcji działanie po prostu 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.
źródło
Aby bezpośrednio odpowiedzieć na oryginalne pytanie. saveInstancestate ma wartość NULL, ponieważ Twoja aktywność nigdy nie jest odtwarzana ponownie.
Twoja aktywność zostanie odtworzona z pakietem stanu tylko wtedy, gdy:
Android niszczy działania w tle, gdy są pod presją pamięci lub po dłuższym przebywaniu w tle.
Podczas testowania przykładu z Witaj świecie istnieje kilka sposobów na opuszczenie i powrót do działania.
W większości przypadków, jeśli tylko naciskasz przycisk Home, a następnie ponownie uruchamiasz aplikację, nie trzeba będzie ponownie tworzyć aktywności. Istnieje już w pamięci, więc funkcja onCreate () nie zostanie wywołana.
W obszarze Ustawienia -> Opcje programisty dostępna jest opcja o nazwie „Nie zachowuj aktywności”. Po włączeniu Android zawsze niszczy działania i odtwarza je, gdy są w tle. Jest to świetna opcja, aby pozostawić włączone podczas opracowywania, ponieważ symuluje najgorszy scenariusz. (Urządzenie z małą ilością pamięci stale przetwarza twoje działania).
Inne odpowiedzi są cenne, ponieważ uczą cię prawidłowych sposobów przechowywania stanu, ale nie czułem, że tak naprawdę odpowiedzieli DLACZEGO twój kod nie działał w oczekiwany sposób.
źródło
onSaveInstanceState(bundle)
IonRestoreInstanceState(bundle)
metody są użyteczne dla utrzymywania danych tylko podczas obracania ekranu (zmiana orientacji).Oni nie są nawet dobre podczas przełączania między aplikacjami (ponieważ
onSaveInstanceState()
metoda nazywa aleonCreate(bundle)
ionRestoreInstanceState(bundle)
nie jest ponownie wywoływany.Aby uzyskać więcej ruchu utrzymywanie wspólnych upodobań. Przeczytaj ten artykuł
źródło
onCreate
ionRestoreInstanceState
nie są nazywane, ponieważActivity
nie jest w ogóle zniszczona podczas przełączania aplikacji, więc nie ma potrzeby, aby przywrócić wszystko. Android dzwonionSaveInstanceState
na wypadek, gdyby Aktywność uległa później zniszczeniu (co dzieje się ze 100% pewnością podczas obracania ekranu, ponieważ zmieniła się cała konfiguracja urządzenia i Aktywność musi zostać odtworzona od nowa).Mój problem polegał na tym, że potrzebowałem trwałości tylko podczas życia aplikacji (tj. Pojedynczego wykonania, w tym uruchomienia innych poddziałań w ramach tej samej aplikacji i obracania urządzenia itp.). Próbowałem różnych kombinacji powyższych odpowiedzi, ale nie uzyskałem tego, czego chciałem we wszystkich sytuacjach. Ostatecznie udało mi się uzyskać odniesienie do saveInstanceState podczas onCreate:
i użyj tego, aby uzyskać zawartość mojej zmiennej, gdy jej potrzebuję, zgodnie z następującymi zasadami:
Używam
onSaveInstanceState
ionRestoreInstanceState
jak sugerowałem powyżej, ale myślę, że mógłbym lub alternatywnie użyć mojej metody, aby zapisać zmienną, gdy się zmienia (np. UżywającputBoolean
)źródło
Chociaż zaakceptowana odpowiedź jest prawidłowa, istnieje szybsza i łatwiejsza metoda zapisania stanu aktywności na Androidzie za pomocą biblioteki o nazwie Icepick . Icepick to procesor adnotacji, który dba o cały kod płyty wzorcowej używany do zapisywania i przywracania dla ciebie stanu.
Robienie czegoś takiego z Icepick:
Czy to samo, co robienie tego:
Icepick będzie działał z każdym obiektem, który zapisuje swój stan za pomocą
Bundle
.źródło
Po utworzeniu działania wywoływana jest jego metoda onCreate ().
saveInstanceState to obiekt klasy pakietu, który po raz pierwszy ma wartość NULL, ale zawiera wartości podczas odtwarzania. Aby zapisać stan działania, musisz zastąpić onSaveInstanceState ().
umieść swoje wartości w obiekcie pakietu „outState”, takim jak outState.putString („klucz”, „Welcome Back”) i zapisz, wywołując super. Kiedy aktywność zostanie zniszczona, jej stan zostanie zapisany w obiekcie pakietu i może zostać przywrócony po odtworzeniu w onCreate () lub onRestoreInstanceState (). Pakiet odebrany w onCreate () i onRestoreInstanceState () są takie same.
lub
źródło
Istnieją zasadniczo dwa sposoby wprowadzenia tej zmiany.
onSaveInstanceState()
ionRestoreInstanceState()
.android:configChanges="orientation|screenSize"
.Naprawdę nie polecam używać drugiej metody. Ponieważ w jednym z moich doświadczeń powodowało to, że połowa ekranu urządzenia była czarna podczas obracania z portretu na krajobraz i odwrotnie.
Używając pierwszej metody wspomnianej powyżej, możemy utrwalić dane po zmianie orientacji lub każdej zmianie konfiguracji. Znam sposób, w jaki możesz przechowywać dowolny typ danych w obiekcie stanu saveInstance.
Przykład: Rozważ przypadek, jeśli chcesz utrwalić obiekt Json. utwórz klasę modelu z getterami i setterami.
Teraz w swojej aktywności w metodach onCreate i onSaveInstanceState wykonaj następujące czynności. Będzie to wyglądać mniej więcej tak:
źródło
Oto komentarz z odpowiedzi Steve'a Moseleya (autorstwa ToolmakerSteve ), który przedstawia rzeczy w perspektywie (w całości onSaveInstanceState vs onPause, koszt wschodni vs saga zachodni)
źródło
Kod Kotlin:
zapisać:
a następnie w
onCreate()
lubonRestoreInstanceState()
Dodaj wartości domyślne, jeśli nie chcesz mieć Opcjonalnych
źródło
Aby uzyskać dane stanu aktywności zapisane w
onCreate()
, najpierw musisz zapisać dane w saveInstanceState metodą przesłonięciaSaveInstanceState(Bundle savedInstanceState)
.Gdy
SaveInstanceState(Bundle savedInstanceState)
zostanie wywołana metoda niszczenia aktywności i tam zapisujesz dane, które chcesz zapisać. I to samoonCreate()
dzieje się, gdy aktywność zostanie ponownie uruchomiona. (SaveInstanceState nie będzie miało wartości zerowej, ponieważ zapisałeś w niej dane przed zniszczeniem aktywności)źródło
Prostym szybkim rozwiązaniem tego problemu jest użycie IcePick
Najpierw skonfiguruj bibliotekę w
app/build.gradle
Teraz sprawdźmy ten przykład poniżej, jak zapisać stan w Aktywności
Działa dla działań, fragmentów lub dowolnego obiektu, który musi szeregować swój stan w pakiecie (np. ViewPresenters zaprawy)
Icepick może również generować kod stanu instancji dla niestandardowych widoków:
źródło
Nie jestem pewien, czy moje rozwiązanie jest rozczarowane, czy nie, ale używam usługi powiązanej, aby utrzymać stan ViewModel. To, czy przechowujesz go w pamięci w usłudze, czy utrwalasz i pobierasz z bazy danych SQLite, zależy od twoich wymagań. To właśnie robią usługi dowolnego rodzaju, świadczą usługi takie jak utrzymanie stanu aplikacji i abstrakcyjna wspólna logika biznesowa.
Ze względu na ograniczenia związane z pamięcią i przetwarzaniem związane z urządzeniami mobilnymi, widoki systemu Android traktuję podobnie jak stronę internetową. Strona nie utrzymuje stanu, jest jedynie składnikiem warstwy prezentacji, którego jedynym celem jest prezentacja stanu aplikacji i akceptacja danych wprowadzonych przez użytkownika. Najnowsze trendy w architekturze aplikacji internetowych wykorzystują odwieczny wzorzec Model, Widok, Kontroler (MVC), gdzie strona to Widok, dane domeny to model, a kontroler siedzi za usługą internetową. Ten sam wzorzec może być zastosowany w Androidzie z Widokiem, cóż ... Widokem, modelem są dane twojej domeny, a Kontroler jest implementowany jako usługa powiązana z Androidem. Ilekroć chcesz, aby widok współdziałał z kontrolerem, powiąż go przy uruchamianiu / wznawianiu i rozpinaj przy zatrzymaniu / pauzie.
Takie podejście daje dodatkową korzyść z egzekwowania zasady projektowania separacji obaw, ponieważ logika biznesowa aplikacji może zostać przeniesiona do Twojej usługi, co zmniejsza zduplikowaną logikę w wielu widokach i pozwala na wymuszenie kolejnej ważnej zasady projektowania, pojedynczej odpowiedzialności.
źródło
Kotlin
Musisz zastąpić
onSaveInstanceState
ionRestoreInstanceState
przechowywać i odzyskiwać zmienne, które chcesz zachowaćWykres cyklu życia
Przechowuj zmienne
Pobierz zmienne
źródło
Teraz Android zapewnia ViewModels do zapisywania stanu, powinieneś spróbować użyć tego zamiast saveInstanceState.
źródło
Istnieje sposób, aby Android zapisał stany bez implementacji żadnej metody. Po prostu dodaj ten wiersz do deklaracji Manifest in Activity:
To powinno wyglądać tak:
Tutaj możesz znaleźć więcej informacji o tej nieruchomości.
Zaleca się, aby system Android zajął się tym za Ciebie niż obsługa ręczna.
źródło
Co zaoszczędzić, a czego nie?
Czy zastanawiałeś się kiedyś, dlaczego tekst w pliku
EditText
jest zapisywany automatycznie podczas zmiany orientacji? Ta odpowiedź jest dla ciebie.Gdy instancja działania zostanie zniszczona, a system odtworzy nową instancję (na przykład zmianę konfiguracji). Próbuje go odtworzyć przy użyciu zestawu zapisanych danych starego stanu aktywności ( stanu instancji ).
Stan instancji to zbiór par klucz-wartość przechowywany w
Bundle
obiekcie.EditText
ListView
itd.Jeśli potrzebujesz innej zmiennej do zapisania jako części stanu instancji, powinieneś OVERRIDE
onSavedInstanceState(Bundle savedinstaneState)
.Na przykład
int currentScore
w GameActivityWięcej szczegółów na temat onSavedInstanceState (pakiet saveinstaneState) podczas zapisywania danych
Które wybrać do przywrócenia stanu aktywności?
LUB
Obie metody otrzymują ten sam obiekt pakietu, więc tak naprawdę nie ma znaczenia, gdzie napiszesz logikę przywracania. Jedyną różnicą jest to, że w
onCreate(Bundle savedInstanceState)
metodzie będziesz musiał dać kontrolę zerową, podczas gdy w drugim przypadku nie jest to konieczne. Inne odpowiedzi mają już fragmenty kodu. Możesz je polecić.Więcej szczegółów na temat onRestoreInstanceState (pakiet zapisanyinstaneState)
Premia
System
onSaveInstanceState(Bundle savedInstanceState)
jest wywoływany tylko wtedy, gdy użytkownik zamierza wrócić do działania. Na przykład używasz aplikacji X i nagle otrzymujesz połączenie. Przejdziesz do aplikacji dzwoniącego i wrócisz do aplikacji X. W tym przypadkuonSaveInstanceState(Bundle savedInstanceState)
metoda zostanie wywołana.Ale rozważ to, jeśli użytkownik naciśnie przycisk Wstecz. Zakłada się, że użytkownik nie zamierza powrócić do działania, dlatego w tym przypadku system
onSaveInstanceState(Bundle savedInstanceState)
nie będzie go wywoływał. Należy pamiętać, że podczas zapisywania danych należy wziąć pod uwagę wszystkie scenariusze.Ważne linki:
Demonstracja domyślnego zachowania
Oficjalna dokumentacja Androida .
źródło
Teraz warto zrobić 2 sposoby w modelu widoku. jeśli chcesz zapisać pierwszy jako zapisaną instancję: Możesz dodać parametr stanu do modelu widoku takiego jak ten 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 momentu zniszczenia działania.
źródło
możesz użyć
Live Data
iView Model
dla Lifecycle Handel
FromJetPack
. zobacz ten odnośnik:https://developer.android.com/topic/libraries/architecture/livedata
źródło