Modul „Saved State“ für ViewModel (Views) Teil von Android Jetpack.
Konzepte und Jetpack Compose-Implementierung
Wie unter UI-Zustände speichern beschrieben, können ViewModel-Objekte Konfigurationsänderungen verarbeiten, sodass Sie sich keine Gedanken über den Zustand bei Rotationen oder anderen Fällen machen müssen. Wenn Sie jedoch den vom System initiierten Prozessabbruch verarbeiten müssen, sollten Sie die SavedStateHandle API als Backup verwenden.
Der UI-Status wird normalerweise in ViewModel-Objekten gespeichert oder darauf verwiesen und nicht in Aktivitäten. Die Verwendung von onSaveInstanceState() erfordert daher etwas Boilerplate-Code, den das Modul für gespeicherten Status für Sie übernehmen kann.
Wenn Sie dieses Modul verwenden, erhalten ViewModel-Objekte über ihren Konstruktor ein SavedStateHandle-Objekt. Dieses Objekt ist eine Schlüssel/Wert-Zuordnung, mit der Sie Objekte in den gespeicherten Status schreiben und daraus abrufen können. Diese Werte bleiben auch dann erhalten, wenn der Prozess vom System beendet wird, und sind weiterhin über dasselbe Objekt verfügbar.
Der gespeicherte Status ist an Ihren Aufgabenstapel gebunden. Wenn Ihr Aufgabenstapel verschwindet, geht auch der gespeicherte Status verloren. Das kann passieren, wenn Sie eine App erzwingen, sie aus dem Menü „Letzte Apps“ entfernen oder das Gerät neu starten. In solchen Fällen verschwindet der Task-Stack und Sie können die Informationen im gespeicherten Zustand nicht wiederherstellen. In Szenarien mit vom Nutzer initiiertem Schließen des UI-Zustands wird der gespeicherte Zustand nicht wiederhergestellt. In vom System initiierten Szenarien ist das der Fall.
Einrichtung
Ab Fragment 1.2.0 oder der transitiven Abhängigkeit Activity 1.1.0 können Sie ein SavedStateHandle als Konstruktorargument für Ihr ViewModel akzeptieren.
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
Anschließend können Sie eine Instanz von ViewModel ohne zusätzliche Konfiguration abrufen. Die Standard-ViewModel-Factory stellt die passende SavedStateHandle für Ihre ViewModel bereit.
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); ... } ... }
Wenn Sie eine benutzerdefinierte ViewModelProvider.Factory-Instanz bereitstellen, können Sie die Verwendung von SavedStateHandle aktivieren, indem Sie AbstractSavedStateViewModelFactory erweitern.
Mit SavedStateHandle arbeiten
Die Klasse SavedStateHandle ist eine Schlüssel/Wert-Zuordnung, mit der Sie über die Methoden set() und get() Daten in den gespeicherten Zustand schreiben und daraus abrufen können.
Wenn Sie SavedStateHandle verwenden, bleibt der Abfragewert auch nach dem Beenden des Prozesses erhalten. So sehen Nutzer vor und nach dem erneuten Erstellen dieselben gefilterten Daten, ohne dass die Aktivität oder das Fragment den Wert manuell speichern, wiederherstellen und an ViewModel weiterleiten muss.
SavedStateHandle hat auch andere Methoden, die Sie bei der Interaktion mit einer Schlüssel/Wert-Zuordnung erwarten würden:
contains(String key): Prüft, ob ein Wert für den angegebenen Schlüssel vorhanden ist.remove(String key): Entfernt den Wert für den angegebenen Schlüssel.keys(): Gibt alle Schlüssel zurück, die inSavedStateHandleenthalten sind.
Außerdem können Sie Werte aus SavedStateHandle mit einem beobachtbaren Daten-Holder abrufen. Folgende Typen werden unterstützt:
LiveData
Ruft Werte aus SavedStateHandle ab, die in einem LiveData-Observable mit getLiveData() umschlossen sind. Wenn der Wert des Schlüssels aktualisiert wird, empfängt LiveData den neuen Wert. Der Wert wird meist aufgrund von Nutzerinteraktionen festgelegt, z. B. durch Eingabe einer Suchanfrage zum Filtern einer Datenliste. Dieser aktualisierte Wert kann dann verwendet werden, um LiveData zu transformieren.
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); } }
Unterstützte Typen
Daten, die in einem SavedStateHandle gespeichert sind, werden als Bundle zusammen mit dem Rest des savedInstanceState für die Aktivität oder das Fragment gespeichert und wiederhergestellt.
Nicht serialisierbare Klassen speichern
Wenn eine Klasse Parcelable oder Serializable nicht implementiert und nicht so geändert werden kann, dass eines dieser Interfaces implementiert wird, ist es nicht möglich, eine Instanz dieser Klasse direkt in einem SavedStateHandle zu speichern.
Ab Lifecycle 2.3.0-alpha03 können Sie mit SavedStateHandle jedes Objekt speichern, indem Sie Ihre eigene Logik zum Speichern und Wiederherstellen Ihres Objekts als Bundle mit der Methode setSavedStateProvider() bereitstellen.
SavedStateRegistry.SavedStateProvider ist eine Schnittstelle, die eine einzelne saveState()-Methode definiert, die ein Bundle mit dem Status zurückgibt, den Sie speichern möchten. Wenn SavedStateHandle bereit ist, seinen Status zu speichern, ruft es saveState() auf, um Bundle aus SavedStateProvider abzurufen, und speichert Bundle für den zugehörigen Schlüssel.
Nehmen wir als Beispiel eine App, die über den Intent ACTION_IMAGE_CAPTURE ein Bild von der Kamera-App anfordert und eine temporäre Datei übergibt, in der die Kamera das Bild speichern soll. Die Logik zum Erstellen dieser temporären Datei ist in TempFileViewModel enthalten.
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; } }
Damit die temporäre Datei nicht verloren geht, wenn der Prozess der Aktivität beendet und später wiederhergestellt wird, kann TempFileViewModel die SavedStateHandle verwenden, um ihre Daten beizubehalten. Damit TempFileViewModel seine Daten speichern kann, implementieren Sie SavedStateProvider und legen Sie sie als Anbieter für SavedStateHandle von ViewModel fest:
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; } } }
Wenn der Nutzer zurückkehrt, rufen Sie die temp_file
Bundle aus dem SavedStateHandle ab, um die File-Daten wiederherzustellen. Dies ist dieselbe Bundle, die von saveTempFile() bereitgestellt wird und den absoluten Pfad enthält. Der absolute Pfad kann dann verwendet werden, um eine neue File zu instanziieren.
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; } } }
Empfehlungen für Sie
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- UI-Zustände speichern
- Mit beobachtbaren Datenobjekten arbeiten
- ViewModels mit Abhängigkeiten erstellen