Salvataggio dello stato con i frammenti

Diverse operazioni del sistema Android possono influire sullo stato del frammento. Per garantire che lo stato dell'utente venga salvato, il framework Android salva e ripristina automaticamente i frammenti e lo stack di back. Pertanto, devi assicurarti che anche tutti i dati nel frammento vengano salvati e ripristinati.

La tabella seguente illustra le operazioni che causano la perdita di stato del frammento e indica se i vari tipi di stato persistono anche dopo aver apportato queste modifiche. I tipi di stato menzionati nella tabella sono i seguenti:

  • Variabili: variabili locali nel frammento.
  • Stato visualizzazione: tutti i dati di proprietà di una o più viste nel frammento.
  • SalvatoState: dati inerenti a questa istanza del frammento che devono essere salvati in onSaveInstanceState().
  • NonConfig: dati estratti da un'origine esterna, ad esempio un server o un repository locale, oppure dati creati dall'utente che vengono inviati a un server dopo il commit.

Spesso le variabili vengono trattate come SalvaState, ma la seguente tabella distingue le due cose per dimostrare l'effetto delle varie operazioni su ognuna.

Funzionamento Variabili Stato visualizzazione StatoSalvato Non configurazione
Aggiunto allo stack precedente x
Modifica della configurazione x
Processo di morte/ricreazione x ✓*
Rimossa non aggiunta allo stack posteriore x x x x
Host terminato x x x x

* Lo stato NonConfig può essere mantenuto dopo l'interruzione del processo utilizzando il modulo Stato salvato per ViewModel.

Tabella 1: varie operazioni distruttive relative ai frammenti e relativi effetti su diversi tipi di stato.

Vediamo un esempio specifico. Prendiamo in considerazione una schermata che genera una stringa casuale, la visualizza in un TextView e offre un'opzione per modificare la stringa prima di inviarla a un amico:

app generatore di testo casuale che dimostra vari tipi di stato
Figura 1. App per la generazione di testo casuale che dimostra vari tipi di stato.

In questo esempio, supponiamo che, dopo che l'utente preme il pulsante di modifica, l'app mostri una vista EditText in cui l'utente può modificare il messaggio. Se l'utente fa clic su ANNULLA, la visualizzazione EditText deve essere cancellata e la visibilità è impostata su View.GONE. Una schermata di questo tipo potrebbe richiedere la gestione di quattro tipi di dati per garantire un'esperienza senza interruzioni:

Dati Tipo Tipo di stato Descrizione
seed Long Non configurazione Seme utilizzato per generare casualmente una nuova buona azione. Viene generato quando viene creato il campo ViewModel.
randomGoodDeed String SaveState + Variabile Generato quando il frammento viene creato per la prima volta. randomGoodDeed viene salvato per garantire che gli utenti vedano le stesse azioni casuali anche dopo la morte e la ricreazione del processo.
isEditing Boolean SaveState + Variabile Contrassegno booleano impostato su true quando l'utente inizia la modifica. isEditing viene salvato per garantire che la parte di modifica dello schermo rimanga visibile quando il frammento viene ricreato.
Testo modificato Editable Visualizza stato (di proprietà di EditText) Il testo modificato nella visualizzazione EditText. La visualizzazione EditText salva questo testo per garantire che le modifiche in corso dell'utente non vadano perse.

Tabella 2: indica che deve essere gestita dall'app di generazione di testo casuale.

Le seguenti sezioni descrivono come gestire correttamente lo stato dei dati tramite operazioni distruttive.

Visualizza stato

Le viste sono responsabili della gestione del proprio stato. Ad esempio, quando una vista accetta l'input utente, è responsabilità della vista salvare e ripristinare l'input per gestire le modifiche alla configurazione. Tutte le viste fornite dal framework Android hanno la propria implementazione di onSaveInstanceState() e onRestoreInstanceState(), quindi non è necessario gestire lo stato di visualizzazione all'interno del frammento.

Ad esempio, nello scenario precedente, la stringa modificata viene archiviata in un elemento EditText. Un EditText conosce il valore del testo visualizzato e altri dettagli, come l'inizio e la fine di qualsiasi testo selezionato.

Una vista richiede un ID per mantenere il proprio stato. Questo ID deve essere univoco all'interno del frammento e nella relativa gerarchia delle visualizzazioni. Le visualizzazioni senza un ID non possono mantenere il loro stato.

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

Come indicato nella tabella 1, le viste salvano e ripristinano il proprio ViewState tramite tutte le operazioni che non rimuovono il frammento o eliminano l'host.

SavedState

Il frammento è responsabile della gestione di piccole quantità di stato dinamico che sono parte integrante del funzionamento del frammento. Puoi conservare i dati facilmente serializzati utilizzando Fragment.onSaveInstanceState(Bundle). Come per Activity.onSaveInstanceState(Bundle), i dati inseriti nel bundle vengono conservati attraverso modifiche alla configurazione ed eliminazione e ricreazione dei processi e sono disponibili nei metodi onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle) e onViewCreated(View, Bundle) del frammento.

Continuando con l'esempio precedente, randomGoodDeed è l'atto mostrato all'utente, mentre isEditing è un flag per determinare se il frammento mostra o nasconde EditText. Questo stato salvato deve essere mantenuto utilizzando onSaveInstanceState(Bundle), come mostrato nell'esempio seguente:

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

Per ripristinare lo stato in onCreate(Bundle), recupera il valore archiviato dal bundle:

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

Come indicato nella tabella 1, tieni presente che le variabili vengono conservate quando il frammento viene posizionato nel backstack. Trattarli come stati salvati garantisce che permangano in tutte le operazioni distruttive.

Non configurazione

I dati NonConfig devono essere posizionati all'esterno del frammento, ad esempio in una ViewModel. Nell'esempio precedente riportato sopra, seed (il nostro stato NonConfig) viene generato in ViewModel. La logica per mantenere il proprio stato è di proprietà di 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;
    }
}

La classe ViewModel consente intrinsecamente che i dati sopravvivono alle modifiche alla configurazione, come le rotazioni dello schermo, e rimangono in memoria quando il frammento viene inserito nello stack posteriore. Dopo la morte e la ricreazione del processo, il ViewModel viene ricreato e viene generato un nuovo seed. L'aggiunta di un modulo SavedState a ViewModel consente a ViewModel di mantenere lo stato semplice attraverso processi di morte e attività ricreative.

Risorse aggiuntive

Per ulteriori informazioni sulla gestione dello stato dei frammenti, consulta le seguenti risorse aggiuntive.

Codelab

Guide