Zapisywanie stanu z fragmentami

Stan fragmentu mogą wpływać różne operacje systemu Android. Aby zachować stan użytkownika, platforma Androida automatycznie zapisuje i przywraca fragmenty oraz stos apek. Dlatego musisz się upewnić, że wszystkie dane we fragmencie również zostały zapisane i przywrócone.

Tabela poniżej zawiera operacje, które powodują utratę stanu przez fragment, oraz informacje o tym, czy różne typy stanu są zachowywane podczas tych zmian. Typy stanów wymienione w tabeli:

  • Zmienne: zmienne lokalne w fragmentach.
  • Stan widoku: dowolne dane, które należą do co najmniej jednego widoku danych we fragmencie.
  • SavedState: dane związane z tą instancją fragmentu, które powinny zostać zapisane w onSaveInstanceState().
  • NonConfig: dane pobrane ze źródła zewnętrznego, np. z serwera lub lokalnego repozytorium, albo dane utworzone przez użytkownika, które po zatwierdzeniu są wysyłane na serwer.

Zmienne są często traktowane tak samo jak SavedState, ale w poniższej tabeli przedstawiono między nimi różnice, aby zaprezentować wpływ na każdą z nich różnych operacji.

Operacja Zmienne Wyświetlanie stanu SavedState Brak konfiguracji
Dodano do grupy elementów do cofnięcia x
Zmiana konfiguracji x
Proces śmierci/wypoczynku x ✓*
Usunięto nie dodano do grupy wstecz x x x x
Host zakończony x x x x

* Stan niebędący stanem konfiguracji może być zachowany po zakończeniu działania procesu za pomocą modułu zapisanego stanu dla ViewModel.

Tabela 1.: różne operacje niszczenia fragmentów i ich wpływ na różne typy stanów.

Przyjrzyjmy się konkretnemu przykładowi. Weźmy na przykład ekran, który generuje losowy ciąg znaków, wyświetla go w TextView i umożliwia jego edycję przed wysłaniem go do znajomego:

Aplikacja do losowego generowania tekstu, która demonstruje różne typy stanu
Rysunek 1. Aplikacja do losowego generowania tekstu, która demonstruje różne stany.

W tym przykładzie zakładamy, że po naciśnięciu przez użytkownika przycisku edycji aplikacja wyświetla widok EditText, w którym użytkownik może edytować wiadomość. Jeśli użytkownik kliknie ANULUJ, widok EditText powinien zostać wyczyszczony i był ustawiony na View.GONE. Taki ekran może wymagać zarządzania 4 rodzajami danych, aby zapewnić płynne działanie:

Dane Typ Typ stanu Opis
seed Long NonConfig Ziarno służące do losowego generowania nowych dobrych uczynków. Generowany podczas tworzenia ViewModel.
randomGoodDeed String SavedState + zmienna Generowany, gdy fragment jest tworzony po raz pierwszy. randomGoodDeed jest zapisywany, aby użytkownicy widzieli tę samą losową dobrą rzecz nawet po zakończeniu i powtórnym uruchomieniu procesu.
isEditing Boolean SavedState + Variable Flaga logiczna ustawiona na true, gdy użytkownik rozpoczyna edycję. Pole isEditing jest zapisywane, aby obszar ekranu, na którym można edytować dane, był widoczny po odtworzeniu fragmentu.
Edytowany tekst Editable Wyświetl stan (właściciel: EditText) Edytowany tekst w widoku EditText. Widok EditText zapisuje ten tekst, aby mieć pewność, że wprowadzane przez użytkownika zmiany nie zostaną utracone.

Tabela 2. Stany, którymi musi zarządzać aplikacja losowego generatora tekstu.

W następnych sekcjach opisaliśmy, jak prawidłowo zarządzać stanem danych za pomocą operacji niszczycielskich.

Wyświetlanie stanu

Wyświetlenia odpowiadają za zarządzanie własnym stanem. Jeśli na przykład widok przyjmuje dane wejściowe od użytkownika, jego zadaniem jest zapisywanie i przywracanie tych danych w celu obsługi zmian konfiguracji. Wszystkie widoki udostępniane przez platformę Android mają własne implementacje funkcji onSaveInstanceState()onRestoreInstanceState(), więc nie musisz zarządzać stanem widoku w fragmentach.

Na przykład w poprzednim scenariuszu edytowany ciąg znaków jest przechowywany w EditText. EditText zna wartość wyświetlanego tekstu, a także inne szczegóły, takie jak początek i koniec dowolnego wybranego tekstu.

Wyświetlanie wymaga identyfikatora, aby zachować stan. Identyfikator musi być niepowtarzalny w ramach fragmentu i jego hierarchii widoków. Widoki bez identyfikatora nie mogą zachować swojego stanu.

<EditText
    android:id="@+id/good_deed_edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

Jak wspomnieliśmy w tabeli 1, widoki danych zapisują i przywracają ViewState za pomocą wszystkich operacji, które nie powodują usunięcia fragmentu ani zniszczenia hosta.

SavedState

Fragment jest odpowiedzialny za zarządzanie niewielkimi ilościami stanu dynamicznego, które są integralną częścią jego działania. Fragment.onSaveInstanceState(Bundle) pozwala łatwo przechowywać dane w szeregu. Podobnie jak w przypadku pakietu Activity.onSaveInstanceState(Bundle) dane umieszczone w bundle’u są zachowywane podczas zmian konfiguracji oraz zatrzymywania i tworzenia procesów. Są one dostępne w metodach onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle)onViewCreated(View, Bundle) fragmentu.

.

W przypadku poprzedniego przykładu randomGoodDeed to akt własności, który jest wyświetlany użytkownikowi, a isEditing to flaga określająca, czy fragment ma wyświetlać czy ukrywać EditText. Zapisany stan powinien być utrwalony za pomocą parametru onSaveInstanceState(Bundle), jak w tym przykładzie:

Kotlin

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putBoolean(IS_EDITING_KEY, isEditing)
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed)
}

Java

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(IS_EDITING_KEY, isEditing);
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed);
}

Aby przywrócić stan w onCreate(Bundle), pobierz zapisaną wartość z paczki:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false)
    randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY)
            ?: viewModel.generateRandomGoodDeed()
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        isEditing = savedInstanceState.getBoolean(IS_EDITING_KEY, false);
        randomGoodDeed = savedInstanceState.getString(RANDOM_GOOD_DEED_KEY);
    } else {
        randomGoodDeed = viewModel.generateRandomGoodDeed();
    }
}

Jak wspomnieliśmy w tabeli 1, zmienne są zachowywane, gdy fragment jest umieszczony na stosie backendowym. Gdy traktujesz je jako zapisane, zapewniasz, że nie powodują żadnej destrukcyjnej operacji.

NonConfig

Dane spoza konfiguracji powinny znajdować się poza fragmentem, na przykład w elemencie ViewModel. W poprzednim przykładzie seed (stan NonConfig) jest generowany w elementach ViewModel. Logika utrzymywania stanu należy do ViewModel.

Kotlin

public class RandomGoodDeedViewModel : ViewModel() {
    private val seed = ... // Generate the seed

    private fun generateRandomGoodDeed(): String {
        val goodDeed = ... // Generate a random good deed using the seed
        return goodDeed
    }
}

Java

public class RandomGoodDeedViewModel extends ViewModel {
    private Long seed = ... // Generate the seed

    private String generateRandomGoodDeed() {
        String goodDeed = ... // Generate a random good deed using the seed
        return goodDeed;
    }
}

Klasa ViewModel pozwala zachować dane w przypadku zmian konfiguracji, takich jak obracanie ekranu, i pozostają w pamięci, gdy fragment jest umieszczony na stosie. Po zakończeniu procesu i jego ponownym uruchomieniu ViewModel zostanie utworzona ponownie, a nowy seed zostanie wygenerowany. Dodanie modułu SavedState do ViewModel pozwala ViewModel zachować prosty stan podczas zabijania i tworzenia procesu.

Dodatkowe materiały

Więcej informacji o zarządzaniu stanem fragmentu znajdziesz w tych dodatkowych materiałach.

Ćwiczenia z programowania

Przewodniki