Modul „Saved State“ für ViewModel Teil von Android Jetpack
Wie bereits erwähnt,
Benutzeroberflächenstatus speichern,
ViewModel
-Objekte können
Konfigurationsänderungen. Sie müssen sich also keine Gedanken über den Status in Rotationen machen.
oder anderen Fällen. Wenn Sie jedoch einen
vom System initiierten Prozess
Tod ist, können Sie die SavedStateHandle
API als Sicherung verwenden.
Der UI-Status wird normalerweise in ViewModel
-Objekten gespeichert oder referenziert, nicht
Aktivitäten. Daher sind für die Verwendung von onSaveInstanceState()
oder rememberSaveable
Textbaustein, der das Modul für gespeicherte Status
das für Sie übernehmen kann.
Bei Verwendung dieses Moduls erhalten ViewModel
-Objekte ein
Objekt SavedStateHandle
über seinen Konstruktor abrufen. Dieses Objekt ist eine Schlüssel/Wert-Zuordnung, mit der Sie
Objekte in den und aus dem gespeicherten Status schreiben bzw. daraus abrufen. Diese Werte
bleiben bestehen, nachdem der Prozess vom System beendet wurde, und weiterhin verfügbar sind.
über dasselbe Objekt.
Der gespeicherte Status ist an Ihren Aufgabenstack gebunden. Wenn Ihr Aufgabenstapel verschwindet, werden Ihre gespeicherten verschwindet. Dies kann passieren, wenn das Beenden einer App erzwungen, aus dem Menü „Zuletzt verwendet“ aus oder starte das Gerät neu. In solchen Fällen muss die Aufgabe verschwindet und Sie können die Informationen nicht im gespeicherten Zustand wiederherstellen. In Vom Nutzer initiierte Ablehnung des UI-Status wird der gespeicherte Status nicht wiederhergestellt. In vom System initiiert und Szenarien.
Einrichten
Ab Fragment 1.2.0
oder seine transitive Abhängigkeit
Aktivität 1.1.0, können Sie
SavedStateHandle
als Konstruktorargument für Ihre ViewModel
festlegen.
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
Sie können dann eine Instanz Ihrer ViewModel
ohne zusätzliche
Konfiguration. Die standardmäßige ViewModel
-Factory stellt den entsprechenden
SavedStateHandle
auf Ihr 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); ... } ... }
Wenn Sie eine benutzerdefinierte
ViewModelProvider.Factory
Instanz können Sie die Nutzung von SavedStateHandle
aktivieren, indem Sie
AbstractSavedStateViewModelFactory
Mit SavedStateHandle arbeiten
Die SavedStateHandle
-Klasse ist eine Schlüssel/Wert-Zuordnung, mit der Sie schreiben und
Daten aus dem gespeicherten Zustand über die
set()
und get()
.
Durch die Verwendung von SavedStateHandle
wird der Abfragewert über den Prozessbeendigung hinaus beibehalten.
Der Nutzer sieht vor und nach der Änderung
denselben Satz gefilterter Daten.
-Neuerstellung, ohne dass die Aktivität oder das Fragment manuell gespeichert, wiederhergestellt
und leiten diesen Wert zurück an ViewModel
.
SavedStateHandle
hat auch andere Methoden, die du bei der Interaktion erwarten kannst
Schlüssel/Wert-Paar-Zuordnung:
contains(String key)
Prüft, ob für den angegebenen Schlüssel ein Wert vorhanden ist.remove(String key)
Entfernt den Wert für den angegebenen Schlüssel.keys()
– Rückgaben alle Schlüssel, die inSavedStateHandle
enthalten sind.
Außerdem können Sie Werte aus SavedStateHandle
abrufen, indem Sie einen
für beobachtbare Daten. Folgende Typen werden unterstützt:
LiveData
Werte aus SavedStateHandle
abrufen, die in ein
LiveData
beobachtbar mit
getLiveData()
Wenn der Wert des Schlüssels aktualisiert wird, erhält LiveData
den neuen Wert. Meiste
wird der Wert häufig aufgrund von Nutzerinteraktionen festgelegt, z. B. der Eingabe einer Anfrage an
um eine Datenliste zu filtern. Mit diesem aktualisierten Wert können Sie dann
LiveData
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); } }
Zustandsfluss
Werte aus SavedStateHandle
abrufen, die in ein
StateFlow
beobachtbar mit
getStateFlow()
Wenn Sie den Wert des Schlüssels aktualisieren, erhält StateFlow
den neuen Wert. Meiste
Häufig wird der Wert aufgrund von Nutzerinteraktionen festgelegt, z. B. der Eingabe eines
zum Filtern einer Datenliste. Sie können diesen aktualisierten Wert dann
mit anderen Flow-Operatoren.
Kotlin
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { val filteredData: StateFlow<List<String>> = savedStateHandle.getStateFlow<String>("query") .flatMapLatest { query -> repository.getFilteredData(query) } fun setQuery(query: String) { savedStateHandle["query"] = query } }
Unterstützung für experimentelle Funktionen von „Compose“
Das Artefakt lifecycle-viewmodel-compose
stellt das experimentelle
saveable
APIs für die Interoperabilität zwischen SavedStateHandle
und Compose
Saver
, sodass alle State
kann speichern über rememberSaveable
mit einem benutzerdefinierten Saver
können auch mit SavedStateHandle
gespeichert werden.
Kotlin
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { var filteredData: List<String> by savedStateHandle.saveable { mutableStateOf(emptyList()) } fun setQuery(query: String) { withMutableSnapshot { filteredData += query } } }
Unterstützte Typen
Daten in einem SavedStateHandle
werden gespeichert und wiederhergestellt als
Bundle
, zusammen mit dem Rest der
savedInstanceState
für die Aktivität oder das Fragment.
Direkt unterstützte Typen
Standardmäßig können Sie set()
und get()
auf einem SavedStateHandle
für die
dieselben Datentypen wie Bundle
, wie unten gezeigt:
Support für Typen/Klassen | Arrayunterstützung |
double |
double[] |
int |
int[] |
long |
long[] |
String |
String[] |
byte |
byte[] |
char |
char[] |
CharSequence |
CharSequence[] |
float |
float[] |
Parcelable |
Parcelable[] |
Serializable |
Serializable[] |
short |
short[] |
SparseArray |
|
Binder |
|
Bundle |
|
ArrayList |
|
Size (only in API 21+) |
|
SizeF (only in API 21+) |
Wenn die Klasse eine der in der obigen Liste genannten nicht erweitert, sollten Sie
die Klasse parcelable durch Hinzufügen der Klasse @Parcelize
Kotlin-Anmerkungen oder -Implementierungen
Parcelable
direkt.
Nicht-parzellierbare Klassen speichern
Wenn eine Klasse Parcelable
oder Serializable
nicht implementiert und nicht
geändert wurden, um eine dieser Schnittstellen zu implementieren,
eine Instanz dieser Klasse direkt in einer SavedStateHandle
speichern.
Beginnend mit
Lebenszyklus 2.3.0-alpha03
Mit SavedStateHandle
können Sie jedes Objekt speichern, indem Sie Ihr eigenes
zum Speichern und Wiederherstellen des Objekts als
Bundle
mithilfe des
setSavedStateProvider()
. SavedStateRegistry.SavedStateProvider
ist eine Schnittstelle, die ein einzelnes
saveState()
, die einen Bundle
mit dem zu speichernden Status zurückgibt. Wann?
SavedStateHandle
kann den Status speichern und ruft saveState()
auf
um die Bundle
aus SavedStateProvider
abzurufen und die
Bundle
für den zugehörigen Schlüssel.
Hier ein Beispiel für eine App, die ein Bild von der Kamera-App über
ACTION_IMAGE_CAPTURE
Intent erstellt und eine temporäre Datei für den Speicherort der Kamera
Bild. TempFileViewModel
enthält die Logik zum Erstellen dieses
temporäre Datei.
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; } }
Um sicherzustellen, dass die temporäre Datei nicht verloren geht, wenn der Prozess der Aktivität beendet wird
und später wiederhergestellt, kann TempFileViewModel
mit SavedStateHandle
Folgendes tun:
ihre Daten beibehalten. Damit TempFileViewModel
Daten speichern kann, implementieren Sie
SavedStateProvider
und legen Sie es als Anbieter im SavedStateHandle
von
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; } } }
Wenn du die File
-Daten wiederherstellen möchtest, wenn der Nutzer zurückkehrt, rufe den temp_file
ab.
Bundle
vom SavedStateHandle
. Dies ist die gleiche Bundle
, die von
saveTempFile()
, der den absoluten Pfad enthält. Der absolute Pfad kann dann
wird verwendet, 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; } } }
SavedStateHandle in Tests
Zum Testen einer ViewModel
, die eine SavedStateHandle
als Abhängigkeit annimmt, erstellen Sie
eine neue Instanz von SavedStateHandle
mit den erforderlichen Testwerten und übergeben
in die ViewModel
-Instanz, die Sie testen.
Kotlin
class MyViewModelTest { private lateinit var viewModel: MyViewModel @Before fun setup() { val savedState = SavedStateHandle(mapOf("someIdArg" to testId)) viewModel = MyViewModel(savedState = savedState) } }
Weitere Informationen
Weitere Informationen zum Modul „Saved State“ für ViewModel
finden Sie in der
in den folgenden Ressourcen.
Codelabs
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- UI-Status speichern
- Mit beobachtbaren Datenobjekten arbeiten
- ViewModels mit Abhängigkeiten erstellen