Modulo Saved State per ViewModel (Views) Parte di Android Jetpack.
Concetti e implementazione di Jetpack Compose
Come indicato in Salvataggio degli stati dell'interfaccia utente, ViewModel oggetti possono gestire
le modifiche alla configurazione, quindi non devi preoccuparti dello stato nelle rotazioni
o in altri casi. Tuttavia, se devi gestire l'interruzione del processo avviata dal sistema, potresti utilizzare l'API SavedStateHandle come backup.
Lo stato dell'interfaccia utente viene in genere archiviato o a cui si fa riferimento negli oggetti ViewModel e non
nelle attività, quindi l'utilizzo di onSaveInstanceState() richiede un po' di codice standard che il
modulo dello stato salvato può gestire per te.
Quando utilizzi questo modulo, gli oggetti ViewModel ricevono un SavedStateHandle
oggetto tramite il relativo costruttore. Questo oggetto è una mappa chiave-valore che ti consente di scrivere e recuperare oggetti nello stato salvato. Questi valori persistono dopo che il processo viene interrotto dal sistema e rimangono disponibili tramite lo stesso oggetto.
Lo stato salvato è legato allo stack delle attività. Se lo stack delle attività scompare, scompare anche lo stato salvato. Ciò può verificarsi quando forzi l'arresto di un'app, la rimuovi dal menu Recenti o riavvii il dispositivo. In questi casi, lo stack delle attività scompare e non puoi ripristinare le informazioni nello stato salvato. Negli scenari di chiusura dello stato dell'interfaccia utente avviati dall'utente, lo stato salvato non viene ripristinato. Negli scenari avviati dal sistema, invece, sì.
Per lo stato utilizzato nella logica di business, conservalo in un ViewModel e salvalo utilizzandoSavedStateHandle. Per lo stato utilizzato nella
logica dell'interfaccia utente, utilizza l'API onSaveInstanceState nel sistema View.Configurazione
A partire da Fragment 1.2.0 o dalla relativa dipendenza transitiva
Activity 1.1.0, puoi accettare un SavedStateHandle come argomento
del costruttore del tuo ViewModel.
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
Puoi quindi recuperare un'istanza del tuo ViewModel senza alcuna configurazione aggiuntiva. La factory ViewModel predefinita fornisce il SavedStateHandle appropriato al tuo ViewModel.
Kotlin
class MainFragment : Fragment() { val vm: SavedStateViewModel by viewModels() ... }
Java
class MainFragment extends Fragment { private SavedStateViewModel vm; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { vm = new ViewModelProvider(this).get(SavedStateViewModel.class); ... } ... }
Quando fornisci un'istanza ViewModelProvider.Factory personalizzata, puoi
abilitare l'utilizzo di SavedStateHandle estendendo
AbstractSavedStateViewModelFactory.
lifecycle-viewmodel-savedstate e utilizza
SavedStateViewModelFactory come factory.Utilizzare SavedStateHandle
La classe SavedStateHandle è una mappa chiave-valore che ti consente di scrivere e
recuperare dati nello stato salvato tramite i metodi set() e
get().
Utilizzando SavedStateHandle, il valore della query viene mantenuto durante l'interruzione del processo, assicurando che l'utente veda lo stesso insieme di dati filtrati prima e dopo la ricreazione senza che l'attività o il fragment debbano salvare, ripristinare e inoltrare manualmente il valore al ViewModel.
SavedStateHandle salva solo i dati scritti quando la
Activity viene arrestata. Le scritture in SavedStateHandle mentre l'Activity è arrestata non vengono salvate a meno che l'Activity non riceva onStart seguito di nuovo da onStop.SavedStateHandle ha anche altri metodi che potresti aspettarti quando interagisci con una mappa chiave-valore:
contains(String key)- Controlla se esiste un valore per la chiave specificata.remove(String key)- Rimuove il valore per la chiave specificata.keys()- Restituisce tutte le chiavi contenute inSavedStateHandle.
Inoltre, puoi recuperare i valori da SavedStateHandle utilizzando un contenitore di dati osservabile. L'elenco dei tipi supportati è il seguente:
get() e set() di base. Un SavedStateHandle viene salvato quando viene chiamato il
onSaveInstanceState sull'attività o sul fragment connesso.
Ciò significa che, sebbene tu possa continuare ad aggiornare i contenitori di dati osservabili da un SavedStateHandle mentre l'app è in background, tutti gli aggiornamenti dello stato potrebbero andare persi se il processo dell'app viene interrotto prima di tornare in primo piano.LiveData
Recupera i valori da SavedStateHandle racchiusi in un oggetto osservabile LiveData
utilizzando getLiveData(). Quando il valore della chiave viene aggiornato, LiveData riceve il nuovo valore. Molto spesso, il valore viene impostato a causa delle interazioni dell'utente, ad esempio l'inserimento di una query per filtrare un elenco di dati. Questo valore aggiornato
può essere utilizzato per trasformare LiveData.
Kotlin
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { val filteredData: LiveData<List<String>> = savedStateHandle.getLiveData<String>("query").switchMap { query -> repository.getFilteredData(query) } fun setQuery(query: String) { savedStateHandle["query"] = query } }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle savedStateHandle; public LiveData<List<String>> filteredData; public SavedStateViewModel(SavedStateHandle savedStateHandle) { this.savedStateHandle = savedStateHandle; LiveData<String> queryLiveData = savedStateHandle.getLiveData("query"); filteredData = Transformations.switchMap(queryLiveData, query -> { return repository.getFilteredData(query); }); } public void setQuery(String query) { savedStateHandle.set("query", query); } }
Tipi supportati
I dati mantenuti in un SavedStateHandle vengono salvati e ripristinati come un Bundle,
insieme al resto di savedInstanceState per l'attività o
il fragment.
Salvare classi non Parcelable
Se una classe non implementa Parcelable o Serializable e non può essere modificata per implementare una di queste interfacce, non è possibile salvare direttamente un'istanza di quella classe in un SavedStateHandle.
A partire da Lifecycle 2.3.0-alpha03, SavedStateHandle ti consente di salvare
qualsiasi oggetto fornendo la tua logica per salvare e ripristinare l'oggetto come
Bundle utilizzando il setSavedStateProvider() metodo.
SavedStateRegistry.SavedStateProvider è un'interfaccia che definisce un
singolo metodo saveState() che restituisce un Bundle contenente lo stato
che vuoi salvare. Quando SavedStateHandle è pronto per salvare il suo stato, chiama saveState() per recuperare il Bundle da SavedStateProvider e salva il Bundle per la chiave associata.
Considera un esempio di un'app che richiede un'immagine dall'app Fotocamera tramite l'intent ACTION_IMAGE_CAPTURE, passando un file temporaneo in cui la fotocamera deve archiviare l'immagine. TempFileViewModel incapsula la logica per la creazione di questo file temporaneo.
Kotlin
class TempFileViewModel : ViewModel() { private var tempFile: File? = null fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel() { } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } }
Per assicurarsi che il file temporaneo non vada perso se il processo dell'attività viene interrotto e ripristinato in un secondo momento, TempFileViewModel può utilizzare SavedStateHandle per rendere persistenti i dati. Per consentire a TempFileViewModel di salvare i dati, implementa
SavedStateProvider e impostalo come provider su SavedStateHandle di
ViewModel:
Kotlin
private fun File.saveTempFile() = bundleOf("path", absolutePath) class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private var tempFile: File? = null init { savedStateHandle.setSavedStateProvider("temp_file") { // saveState() if (tempFile != null) { tempFile.saveTempFile() } else { Bundle() } } } fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel(SavedStateHandle savedStateHandle) { savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider()); } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider { @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); if (tempFile != null) { bundle.putString("path", tempFile.getAbsolutePath()); } return bundle; } } }
Per ripristinare i dati File quando l'utente torna, recupera il Bundle temp_file da SavedStateHandle. Si tratta dello stesso Bundle fornito da saveTempFile() che contiene il percorso assoluto. Il percorso assoluto può essere utilizzato per creare un nuovo File.
Kotlin
private fun File.saveTempFile() = bundleOf("path", absolutePath) private fun Bundle.restoreTempFile() = if (containsKey("path")) { File(getString("path")) } else { null } class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private var tempFile: File? = null init { val tempFileBundle = savedStateHandle.get<Bundle>("temp_file") if (tempFileBundle != null) { tempFile = tempFileBundle.restoreTempFile() } savedStateHandle.setSavedStateProvider("temp_file") { // saveState() if (tempFile != null) { tempFile.saveTempFile() } else { Bundle() } } } fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel(SavedStateHandle savedStateHandle) { Bundle tempFileBundle = savedStateHandle.get("temp_file"); if (tempFileBundle != null) { tempFile = TempFileSavedStateProvider.restoreTempFile(tempFileBundle); } savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider()); } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider { @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); if (tempFile != null) { bundle.putString("path", tempFile.getAbsolutePath()); } return bundle; } @Nullable private static File restoreTempFile(Bundle bundle) { if (bundle.containsKey("path") { return File(bundle.getString("path")); } return null; } } }
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Salvare gli stati dell'interfaccia utente
- Utilizzare oggetti di dati osservabili
- Creare ViewModel con dipendenze