Zapisywanie stanu z fragmentami

Na stan fragmentu mogą wpływać różne operacje systemu Android. Aby zapewnić zapisanie stanu użytkownika, platforma Androida automatycznie zapisuje i przywraca fragmenty i stos wsteczny. Dlatego musisz dopilnować, aby wszelkie dane z tego fragmentu również zostały zapisane i przywrócone.

W tabeli poniżej zebraliśmy operacje, które powodują utratę stanu fragmentu, a także informacje o tym, czy różne typy stanu utrzymują się po tych zmianach. W tabeli wymienione są następujące typy stanów:

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

Często Zmienne są traktowane tak samo jak SavedState, ale w poniższej tabeli rozróżniono je, aby pokazać wpływ różnych operacji na każdą z nich.

Operacja Zmienne Stan widoku danych SavedState Brak konfiguracji
Dodano do stosu wstecznego x
Zmiana konfiguracji x
Proces śmierci/rekreacji x ✓*
Usunięto, nie dodano do stosu wstecznego x x x x
Host zakończony x x x x

* Stan braku konfiguracji można zachować po zakończeniu zakończenia 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 stanu.

Przeanalizujmy konkretny przykład. Rozważmy ekran, który generuje losowy ciąg znaków, wyświetla go w elemencie TextView i udostępnia opcję edytowania ciągu przed wysłaniem go do znajomego:

aplikacja do generowania losowych tekstów, która prezentuje różne rodzaje stanu;
Rysunek 1. Aplikacja do generowania losowych tekstów demonstrująca różne rodzaje stanu.

W tym przykładzie załóżmy, że gdy użytkownik kliknie przycisk edycji, aplikacja wyświetli widok EditText, w którym użytkownik może edytować wiadomość. Jeśli użytkownik kliknie ANULUJ, widok EditText powinien zostać wyczyszczony, a jego widoczność jest ustawiona na View.GONE. Taki ekran może wymagać zarządzania 4 elementami danych, aby zapewnić bezproblemową obsługę:

Dane Typ Typ stanu Opis
seed Long Brak konfiguracji Nasiona używane do losowego generowania nowego dobrego ustanowienia. Generujony podczas tworzenia ViewModel.
randomGoodDeed String SavedState + zmienna Generujony podczas tworzenia fragmentu po raz pierwszy. Zapis randomGoodDeed jest zapisywany, aby użytkownicy widzieli ten sam losowy dobra czynność nawet po śmierci i regeneracji.
isEditing Boolean SavedState + zmienna Gdy użytkownik rozpoczyna edytowanie, flaga wartości logicznej jest ustawiona na true. Element isEditing jest zapisywany, aby edytowalna część ekranu była widoczna po odtworzeniu fragmentu.
Edytowany tekst Editable Wyświetl stan (będący własnością firmy EditText) Edytowany tekst w widoku EditText. Widok EditText zapisuje ten tekst, aby zapewnić, że zmiany wprowadzane przez użytkownika nie zostaną utracone.

Tabela 2. Wskazuje, że aplikacja do generowania losowych tekstów musi zarządzać aplikacją.

W poniższych sekcjach opisano, jak prawidłowo zarządzać stanem danych za pomocą operacji destrukcyjnych.

Wyświetl stan

Za zarządzanie własnym stanem odpowiadają widoki danych. Jeśli np. widok przyjmuje dane wejściowe użytkownika, jego obowiązkiem jest zapisanie i przywrócenie tych danych w celu obsługi zmian w konfiguracji. Wszystkie widoki udostępniane przez platformę Androida mają własną implementację znaczników onSaveInstanceState() i onRestoreInstanceState(), więc nie musisz zarządzać stanem widoku w obrębie tego fragmentu.

Na przykład w poprzednim scenariuszu edytowany ciąg znaków jest utrzymywany w elemencie EditText. Element EditText zna wartość wyświetlanego tekstu oraz inne szczegóły, takie jak początek i koniec dowolnego zaznaczonego tekstu.

Widok wymaga identyfikatora, aby zachować stan. Ten identyfikator musi być unikalny w obrębie fragmentu i jego hierarchii widoków. Widoki bez identyfikatora nie zachowują swojego stanu.

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

Jak wspomniano w tabeli 1, widoki zapisują i przywracają ViewState we wszystkich operacjach, które nie usuwają fragmentu ani nie niszczą hosta.

SavedState

Twój fragment odpowiada za zarządzanie niewielkimi ilościami stanu dynamicznego, które są integralną częścią jego działania. Dane zserializowane możesz przechowywać w prosty sposób za pomocą narzędzia Fragment.onSaveInstanceState(Bundle). Podobnie jak w przypadku Activity.onSaveInstanceState(Bundle), dane umieszczone w pakiecie są zachowywane przez zmiany konfiguracji, proces śmierci i regeneracji oraz są dostępne w metodach onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle) i onViewCreated(View, Bundle) danego fragmentu.

Kontynuując poprzedni przykład, randomGoodDeed to akt wyświetlany użytkownikowi, a isEditing to flaga określająca, czy fragment pokazuje, czy ukrywa EditText. Ten zapisany stan powinien się utrzymywać 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 pakietu:

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 zostanie umieszczony na stosie wstecznym. Zapisujemy je w stanie, aby zachować trwałość we wszystkich niszczycielskich operacjach.

Brak konfiguracji

Dane nonConfig należy umieszczać poza własnym fragmentem, np. w ViewModel. W poprzednim przykładzie powyżej seed (nasz stan braku konfiguracji) jest generowany w ViewModel. Logika utrzymywania stanu należy do platformy 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 z założenia pozwala danemu przetrwać zmiany konfiguracji, takie jak obrót ekranu, i pozostaje w pamięci, gdy fragment zostanie umieszczony w stosie wstecznym. Po zakończeniu procesu śmierci i reprodukcji klucz ViewModel jest odtworzony i generowany jest nowy seed. DodanieSavedState modułu do ViewModel umożliwia ViewModel zachowanie prostego stanu przez śmierć i odtwarzanie procesu.

Dodatkowe materiały

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

Ćwiczenia z programowania

Przewodniki