Moduł Zapisano stan dla ViewModel Zawiera Android Jetpack.
Jak wspomniano w:
Zapisuję stany interfejsu,
Obiekty ViewModel
, które mogą obsługiwać
zmian konfiguracji, więc nie trzeba martwić się o stan w rotacjach.
ani w innych przypadkach. Jeśli jednak musisz obsłużyć proces zainicjowany przez system
śmiało, może warto użyć interfejsu API SavedStateHandle
jako aplikacji zapasowej.
Stan interfejsu jest zwykle przechowywany lub odwołuje się do obiektów ViewModel
, ale nie
aktywności, dlatego korzystanie z onSaveInstanceState()
lub rememberSaveable
wymaga
widać, że moduł zapisywania stanu
które zrobią za Ciebie.
Podczas korzystania z tego modułu obiekty ViewModel
otrzymują odpowiedź
SavedStateHandle
obiekt
za pomocą jego konstruktora. Ten obiekt to mapa klucz-wartość, która umożliwia
do zapisywania i pobierania obiektów w stanie i z powrotem. Wartości te
utrzymują się po zatrzymaniu procesu przez system i pozostają dostępne
przez ten sam obiekt.
Zapisany stan jest powiązany ze stosem zadań. Jeśli stos zadań zniknie, zapisane również nie jest brany pod uwagę. Może się tak zdarzyć, gdy wymuszane jest zatrzymanie aplikacji, co powoduje usunięcie wybierając aplikację z menu ostatnich lub zrestartuj urządzenie. W takich przypadkach zadanie te dane znikną i nie będzie można przywrócić zapisanych informacji. W Odrzucenie stanu interfejsu inicjowanego przez użytkownika zapisane scenariusze nie zostaną przywrócone. W zainicjowane przez system a w przypadku niektórych scenariuszy.
Konfiguracja
Rozpoczyna się od Fragment 1.2.0
lub jej przejściowej zależności
Aktywności 1.1.0, możesz zaakceptować
SavedStateHandle
jako argumentu konstruktora w ViewModel
.
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
Następnie możesz pobrać instancję ViewModel
bez dodatkowych
konfiguracji. Domyślna fabryka elementu ViewModel
zapewnia odpowiednie
SavedStateHandle
na urządzenie 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); ... } ... }
W przypadku parametru niestandardowego
ViewModelProvider.Factory
możesz włączyć korzystanie z usługi SavedStateHandle
, rozszerzając
AbstractSavedStateViewModelFactory
Praca z SavedStateHandle
Klasa SavedStateHandle
to mapa klucz-wartość, która umożliwia zapisywanie
pobierania danych do i z zapisanego stanu za pomocą funkcji
set()
.
i get()
.
Gdy metoda SavedStateHandle
powoduje, że wartość zapytania jest zachowywana po zakończeniu procesu,
Zapewnia, że użytkownik widzi ten sam zestaw filtrowanych danych przed i po.
odtwarzania bez konieczności ręcznego zapisywania, przywracania
i przekazać ją z powrotem do funkcji ViewModel
.
SavedStateHandle
udostępnia też inne metody interakcji, których możesz się spodziewać
z mapą par klucz-wartość:
contains(String key)
– Sprawdza, czy dany klucz ma wartość.remove(String key)
– Usuwa wartość danego klucza.keys()
– zwroty wszystkich kluczy zawartych wSavedStateHandle
.
Możesz też pobierać wartości z SavedStateHandle
za pomocą
obserwowalnego właściciela danych. Lista obsługiwanych typów to:
Dane aktywne
Pobieranie wartości z zakresu SavedStateHandle
, które są zawarte w pliku
LiveData
dostrzegalne przy użyciu
getLiveData()
Po zaktualizowaniu wartości klucza LiveData
otrzymuje nową wartość. Większość
często wartość jest ustawiana w zależności od interakcji użytkownika, takich jak wpisanie zapytania,
filtrować listę danych. Zaktualizowanej wartości można później użyć do:
przekształcić element 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); } }
StateFlow
Pobieranie wartości z zakresu SavedStateHandle
, które są zawarte w pliku
StateFlow
dostrzegalne przy użyciu
getStateFlow()
Po zaktualizowaniu wartości klucza StateFlow
otrzyma nową wartość. Większość
często możesz określić wartość w zależności od interakcji użytkownika, takich jak wpisanie
aby przefiltrować listę danych. Następnie możesz przekształcić tę zaktualizowaną wartość.
przy użyciu innych operatorów przepływu.
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 } }
Obsługa stanu eksperymentalnego tworzenia wiadomości
Artefakt lifecycle-viewmodel-compose
udostępnia funkcję eksperymentalną
saveable
.
Interfejsy API umożliwiające współdziałanie SavedStateHandle
i Compose
Saver
, dzięki którym każdy State
można zapisać przez rememberSaveable
z niestandardową wartością Saver
można też zapisać w SavedStateHandle
.
Kotlin
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { var filteredData: List<String> by savedStateHandle.saveable { mutableStateOf(emptyList()) } fun setQuery(query: String) { withMutableSnapshot { filteredData += query } } }
Obsługiwane typy
Dane przechowywane w usłudze SavedStateHandle
są zapisywane i przywracane jako
Bundle
, wraz z pozostałymi
savedInstanceState
dla danej aktywności lub fragmentu.
Typy obsługiwane bezpośrednio
Domyślnie możesz dzwonić pod numery set()
i get()
w SavedStateHandle
w celu
tych samych typów danych co Bundle
, jak pokazano poniżej:
Obsługa typu/klasy | Obsługa tablic |
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+) |
Jeśli klasa nie obejmuje żadnej z powyższych list, rozważ utworzenie
klasy, do której można dołączyć, dodając @Parcelize
Adnotacja lub implementacja Kotlin
bezpośrednio Parcelable
.
Zapisywanie klas, których nie można podzielić na pakiet
Jeśli klasa nie zawiera metody Parcelable
lub Serializable
i nie można jej
modyfikacji w celu wdrożenia jednego z tych interfejsów, nie można
bezpośrednio zapisać instancję tej klasy w kolekcji SavedStateHandle
.
Rozpoczyna się od
Cykl życia 2.3.0-alfa03,
SavedStateHandle
umożliwia zapisanie dowolnego obiektu przez podanie własnych
do zapisywania i przywracania obiektu jako
Bundle
za pomocą
setSavedStateProvider()
. SavedStateRegistry.SavedStateProvider
to interfejs, który określa jeden
saveState()
zwracającą Bundle
zawierającą stan, który chcesz zapisać. Kiedy
SavedStateHandle
jest gotowy do zapisania stanu; wywołuje polecenie saveState()
aby pobrać Bundle
z SavedStateProvider
i zapisać
Bundle
dla powiązanego klucza.
Zobacz przykład aplikacji, która wysyła żądanie obrazu z aparatu za pomocą
ACTION_IMAGE_CAPTURE
przez przesłanie pliku tymczasowego zawierającego miejsce, w którym kamera
. TempFileViewModel
zawiera logikę utworzenia
tymczasowy plik.
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; } }
Aby mieć pewność, że plik tymczasowy nie zostanie utracony po zatrzymaniu procesu aktywności
i później ją przywrócona, TempFileViewModel
może używać: SavedStateHandle
do
zachowanie danych. Aby umożliwić aplikacji TempFileViewModel
zapisywanie danych, zaimplementuj
SavedStateProvider
i ustaw ją jako dostawcę w SavedStateHandle
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; } } }
Aby przywrócić dane File
, gdy użytkownik powraca, pobierz temp_file
Bundle
z SavedStateHandle
. To ten sam produkt typu Bundle
, który udostępnia użytkownik:
saveTempFile()
, która zawiera ścieżkę bezwzględną. Ścieżka bezwzględna może wtedy
mogą zostać użyte do utworzenia nowego wystąpienia 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; } } }
SavedStateHandle w testach
Aby przetestować zależność ViewModel
, która wymaga SavedStateHandle
, utwórz
nowe wystąpienie SavedStateHandle
z wymaganymi wartościami testowymi oraz zdanymi
go do testowanej instancji ViewModel
.
Kotlin
class MyViewModelTest { private lateinit var viewModel: MyViewModel @Before fun setup() { val savedState = SavedStateHandle(mapOf("someIdArg" to testId)) viewModel = MyViewModel(savedState = savedState) } }
Dodatkowe materiały
Więcej informacji o module Zapisane stany (ViewModel
) znajdziesz tutaj:
poniższe zasoby.
Ćwiczenia z programowania
.Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Zapisywanie stanów interfejsu
- Praca z obserwowalnymi obiektami danych
- Tworzenie modeli ViewModel z zależnościami