Przywracanie stanu (State Restoration) to mechanizm mobilnych systemów operacyjnych, który pozwala zapisywać i przywracać interfejs użytkownika aplikacji po jej ponownym uruchomieniu lub minimalizacji. System zapisuje stan UI w pamięci lub trwałym magazynie i przywraca go przy ponownym otwarciu. Według danych Android Developers (2025), State Restoration jest obowiązkowy dla aplikacji aspirujących do wysokiej jakości doświadczenia użytkownika. State Restoration jest krytycznie ważny dla zapobiegania utracie danych przy nieoczekiwanym zamknięciu aplikacji.
Najważniejsze
State Restoration (przywracanie stanu) to systemowy mechanizm, który pozwala zapisywać bieżący stan interfejsu użytkownika aplikacji i przywracać go po zamknięciu lub ponownym uruchomieniu. Gdy użytkownik minimalizuje aplikację lub system zamyka ją w celu zwolnienia zasobów, State Restoration rejestruje kluczowe parametry UI i zapisuje je w zaszyfrowanym magazynie.
Bez State Restoration użytkownik traci wszystkie niezapisane dane przy przełączaniu między aplikacjami. Na przykład wypełniony formularz kontaktowy, długie zapytanie wyszukiwania lub częściowo przeglądana lista wiadomości — wszystko to znika przy ponownym uruchomieniu. State Restoration rozwiązuje ten problem, automatycznie rejestrując stan ViewController lub Activity w momencie minimalizacji.
Mechanizm działa na poziomie systemowym i jest obsługiwany przez obie główne platformy mobilne. iOS udostępnia State Restoration przez NSUserActivity i protokół UIStateRestoring, a Android — przez SavedStateHandle w komponentach architektonicznych Jetpack i ViewModel. Implementacja różni się, ale koncepcja jest identyczna.
Proces State Restoration dzieli się na dwie fazy: zapisywanie (save) i przywracanie (restore). W fazie zapisywania system wywołuje odpowiednie metody cyklu życia, w których aplikacja musi serializować bieżący stan UI do zwartej reprezentacji. W fazie przywracania system przekazuje zapisane dane z powrotem, a aplikacja deserializuje je w celu przywrócenia UI.
Zapisywanie jest inicjowane przez system przy przejściu aplikacji do trybu tła lub przy otrzymaniu sygnału o zbliżającym się zamknięciu. W iOS wywoływana jest metoda encodeRestorableState w UIViewController, w Android — onSaveInstanceState w Activity lub zapis przez SavedStateHandle. Dane są serializowane do formatu obsługującego typy prymitywne: ciągi znaków, liczby, tablice bajtów i obiekty Parcelable.
Objętość zapisywanych danych powinna być minimalna — system nakłada ograniczenia na rozmiar zapisywanego pakietu. W Android limit wynosi około 50 KB na proces. Przekroczenie limitu prowadzi do wyjątku TransactionTooLargeException. Dlatego architekci zalecają zapisywanie tylko identyfikatorów i kluczy, a pełne dane ładować z trwałego magazynu przy przywracaniu.
Przy przywracaniu system przekazuje zapisany pakiet danych aplikacji w momencie uruchomienia. W iOS wywoływana jest metoda decodeRestorableState, w Android — onRestoreInstanceState lub odczyt z SavedStateHandle. Aplikacja wyodrębnia identyfikatory i klucze z pakietu i przywraca UI: pozycję przewijania, wybrane elementy, wprowadzone dane.
Ważne jest uwzględnienie, że przywracanie może nastąpić w nowym procesie. Jeśli aplikacja została całkowicie usunięta z pamięci, proces jest tworzony od nowa, a wszystkie obiekty w pamięci są nieobecne. Dlatego stan musi być serializowalny i niezależny od kontekstu uruchomieniowego poprzedniej sesji. Jest to szczególnie krytyczne dla dużych formularzy z wieloma polami wprowadzania i długich wielostronicowych interfejsów.
class MainActivity : AppCompatActivity() {
private var searchQuery: String = ""
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("search_query", searchQuery)
}
override fun onRestoreInstanceState(savedState: Bundle) {
super.onRestoreInstanceState(savedState)
searchQuery = savedState.getString("search_query", "")!!
restoreSearchUI(searchQuery)
}
}
Implementacja State Restoration znacznie różni się między platformami. iOS używa deklaratywnego podejścia przez storyboard i protokoły UIKit, a Android — imperatywnego przez metody cyklu życia Activity i komponenty architektoniczne Jetpack. Wybór podejścia zależy od platformy docelowej i architektury aplikacji.
W iOS State Restoration opiera się na trzech komponentach: UIApplication zarządza całym procesem, UIViewController implementuje protokół UIStateRestoring, a NSUserActivity przechowuje dane do przywracania nawigacji. Aby włączyć, należy ustawić restorationIdentifier w UIViewController i zaimplementować encodeRestorableState oraz decodeRestorableState.
iOS automatycznie zapisuje stan kontrolera nawigacji (UINavigationController) i wszystkich zagnieżdżonych ViewController, jeśli mają ustawiony restorationIdentifier. System zarządza stosem nawigacji i przywraca go w pierwotnym stanie. Jednak dane wewnątrz kontrolerów (wprowadzony tekst, pozycja przewijania) muszą być jawnie zapisane przez programistę.
W Android nowoczesne podejście do State Restoration opiera się na SavedStateHandle — komponencie z biblioteki AndroidX Lifecycle. SavedStateHandle jest dostępny wewnątrz ViewModel i automatycznie zapisuje i przywraca dane przy zmianie konfiguracji (obrót ekranu) i przy ponownym uruchomieniu procesu. Dane są przechowywane w Bundle i automatycznie serializowane.
SavedStateHandle zachowuje się jak magazyn klucz-wartość z obsługą LiveData. Przy zmianie konfiguracji dane są zapisywane i przywracane automatycznie. Aby obsłużyć ponowne uruchomienie procesu, ViewModel musi być utworzony przez SavedStateViewModelFactory — to pozwala ViewModel przetrwać całkowite zamknięcie aplikacji.
class SearchViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
companion object {
private val KEY_QUERY = string("search_query")
}
fun getSearchQuery(): String? = savedStateHandle[KEY_QUERY]
fun saveSearchQuery(query: String) {
savedStateHandle[KEY_QUERY] = query
}
}
Praktyczna implementacja State Restoration wymaga uwzględnienia kilku aspektów: wybór odpowiedniego magazynu, określenie objętości zapisywanych danych i testowanie różnych scenariuszy zamknięcia. Rozważmy implementację krok po kroku dla aplikacji Flutter z użyciem pakietu state_restoration.
class RestorableSearchField extends RestorableProperty<String> {
String _value = '';
@override
String get value => _value;
@override
void set value(String newValue) {
if (_value != newValue) {
_value = newValue;
notifyListeners();
}
}
@override
String? toPrimitives() => _value;
@override
void fromPrimitives(String? data) {
_value = data ?? '';
}
}
Przy implementacji ważne jest pamiętanie o granicach zapisywania. Nie każde pole UI wymaga przywracania. Pozycja przewijania na długiej liście — tak. Tymczasowy stan animacji — nie. Programista powinien świadomie wybierać, które dane są krytyczne dla doświadczenia użytkownika, a które można bezpiecznie zresetować bez utraty wygody.
Testowanie State Restoration to osobne zadanie, wymagające symulacji zamknięcia procesu. Na Android można to zrobić przez polecenie adb shell am kill, na iOS — przez symulację zakończenia w Xcode. Frameworki do testowania UI, takie jak Espresso i XCTest, udostępniają specjalne metody do sprawdzania przywracania stanu.
Pierwsza zasada — zapisuj identyfikatory, a nie dane. Zamiast zapisywać pełny obiekt z setką pól, zapisz jego unikalny identyfikator, a przy przywracaniu załaduj aktualne dane z bazy danych lub API. Oszczędza to miejsce w Bundle i gwarantuje aktualność danych w momencie przywracania.
Druga zasada — testuj wszystkie scenariusze. Sprawdź przywracanie po obrocie ekranu, po minimalizacji i powrocie po godzinie, po zamknięciu aplikacji przez system z powodu braku pamięci. Każdy scenariusz może zachowywać się inaczej w zależności od stanu systemu operacyjnego i dostępnych zasobów.
Trzecia zasada — używaj mechanizmów systemowych, a nie własnych. iOS i Android udostępniają wbudowane API do State Restoration, które są zoptymalizowane pod konkretną platformę. Własna implementacja przez SharedPreferences lub UserDefaults może prowadzić do problemów z synchronizacją i nieoczekiwanego zachowania przy przywracaniu.
Czwarta zasada — obsługuj brak stanu. Przy pierwszym uruchomieniu lub po wyczyszczeniu danych stan może być nieobecny. UI powinien działać poprawnie w stanie początkowym bez rzucania wyjątków. Sprawdzaj wszystkie zapisane dane na null przed użyciem i przewiduj wartości domyślne.
Piąta zasada — dokumentuj zapisywane klucze. Gdy w projekcie są dziesiątki ekranów i każdy zapisuje kilka pól, bez scentralizowanego zarządzania kluczami powstaje chaos. Stwórz jedną klasę lub plik z stałymi kluczy dla State Restoration w każdym module. Upraszcza to utrzymanie i zapobiega przypadkowym nadpisaniom danych przy refaktoryzacji.
Często zadawane pytania
State Restoration — mechanizm zapisywania i przywracania interfejsu użytkownika aplikacji po jej ponownym uruchomieniu lub minimalizacji, zapobiegający utracie danych i kontekstu pracy użytkownika.
State Restoration zapisuje tymczasowy stan UI (pozycję przewijania, wprowadzone dane w formularzu), a baza danych — stałe dane użytkownika. State Restoration używa mechanizmów systemowych (Bundle, NSData) z ograniczeniem objętości.
Użyj SavedStateHandle w ViewModel z AndroidX Lifecycle. Automatycznie zapisuje dane przy minimalizacji i przywraca przy powrocie. Aby obsłużyć pełne ponowne uruchomienie, użyj SavedStateViewModelFactory.
Ustaw restorationIdentifier w UIViewController i zaimplementuj metody encodeRestorableState oraz decodeRestorableState. Do nawigacji użyj NSUserActivity z zapisem ścieżki w stosie kontrolerów.
Zapisuj identyfikatory, a nie pełne dane: ID wybranego elementu, zapytanie wyszukiwania, pozycję przewijania, stan przełączników. Unikaj zapisywania dużych obiektów i obrazów.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również