Salvataggio dello stato con i frammenti

Lo stato del frammento può essere influenzato da varie operazioni di sistema Android. Per garantire che lo stato dell'utente venga salvato, il framework Android salva e ripristina automaticamente i frammenti e il back stack. Pertanto, devi assicurarti che anche i dati nel frammento vengano salvati e ripristinati.

La tabella seguente illustra le operazioni che causano la perdita dello stato del frammento e indica se i vari tipi di stato rimangono invariati durante queste modifiche. I tipi di stato menzionati nella tabella sono i seguenti:

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

Spesso le variabili vengono trattate come le SavedState, ma la tabella seguente distingue tra le due per dimostrare l'effetto delle varie operazioni su ciascuna.

Funzionamento Variabili Visualizza stato SavedState Non-configurazione
Aggiunto alla serie di app aperte precedenti x
Modifica configurazione x
Interruzione del processo/ricreazione x ✓*
Rimozione non aggiunta all'elenco filtri precedente x x x x
Host completato 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 dei frammenti e i relativi effetti su diversi tipi di stato.

Vediamo un esempio specifico. Prendiamo ad esempio una schermata che genera una stringa random, la mostra in un TextView e offre la possibilità di modificarla prima di inviarla a un amico:

App di generazione di testo casuale che mostra vari tipi di stato
Figura 1. App di generazione di testo casuale che mostra diversi tipi di stato.

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

Dati Digitazione Tipo di stato Descrizione
seed Long Non-configurazione Seed utilizzato per generare in modo casuale una nuova buona azione. Generato quando viene creato ViewModel.
randomGoodDeed String SavedState + Variabile Generato quando il frammento viene creato per la prima volta. randomGoodDeed viene salvato per garantire che gli utenti vedano la stessa buona azione casuale anche dopo la morte o la ricreazione del processo.
isEditing Boolean SavedState + variabile Flag 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 Stato della visualizzazione (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 l'app per la generazione di testo casuale.

Le sezioni seguenti 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 dell'utente, è responsabilità della vista salvare e ripristinare questo input per gestire le modifiche alla configurazione. Tutte le visualizzazioni fornite dal framework Android hanno la propria implementazione di onSaveInstanceState() e onRestoreInstanceState(), quindi non devi gestire lo stato della visualizzazione all'interno del tuo frammento.

Ad esempio, nello scenario precedente, la stringa modificata è contenuta in un EditText. Un EditText conosce il valore del testo visualizzato, nonché altri dettagli, come l'inizio e la fine del testo selezionato.

Una vista ha bisogno di un ID per mantenere il proprio stato. Questo ID deve essere univoco all'interno del frammento e della relativa gerarchia di visualizzazione. Le visualizzazioni senza 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 visualizzazioni salvano e ripristinano il proprio ViewState tramite tutte le operazioni che non rimuovono il frammento o distruggono 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 serializzati facilmente utilizzando Fragment.onSaveInstanceState(Bundle). Analogamente a Activity.onSaveInstanceState(Bundle), i dati inseriti nel bundle vengono conservati attraverso modifiche alla configurazione ed elaborano la morte e la ricreazione e sono disponibili nei metodi onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle) e onViewCreated(View, Bundle) del frammento.

Continuando con l'esempio precedente, randomGoodDeed è il documento che viene visualizzato all'utente e isEditing è un flag per determinare se il frammento mostra o nasconde EditText. Questo stato salvato deve essere conservato 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 inserito nella pila precedente. Il trattamento come stato salvato garantisce che rimangano invariati durante tutte le operazioni distruttive.

Non-configurazione

I dati NonConfig devono essere posizionati all'esterno del frammento, ad esempio in un ViewModel. Nell'esempio precedente, in ViewModel viene generato seed (il nostro stato NonConfig). La logica per mantenere lo 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 i dati di sopravvivere alle modifiche alla configurazione, come le rotazioni dello schermo, e rimane in memoria quando il frammento viene posizionato sullo stack posteriore. Dopo l'interruzione e la ricreazione del processo, ViewModel viene ricreato e viene generato un nuovo seed. L'aggiunta di un modulo SavedState al tuo ViewModel consente a ViewModel di mantenere uno stato semplice tramite l'interruzione e la ricreazione del processo.

Risorse aggiuntive

Per ulteriori informazioni sulla gestione dello stato del frammento, consulta le seguenti risorse aggiuntive.

Codelab

Guide