ViewModel için Kaydedilmiş Durum modülü Android Jetpack'in bir parçasıdır.
Kullanıcı Arayüzü Durumlarını Kaydetme bölümünde belirtildiği gibi, ViewModel nesneleri yapılandırma değişikliklerini işleyebilir. Bu nedenle, döndürme veya diğer durumlarda durumla ilgili endişelenmenize gerek yoktur. Ancak sistem tarafından başlatılan süreç sonlandırmalarını işlemeniz gerekiyorsa yedek olarak SavedStateHandle API'sini kullanabilirsiniz.
Kullanıcı arayüzü durumu genellikle ViewModel nesnelerinde depolanır veya bunlara referans verilir. Bu nedenle, Compose'da rememberSaveable kullanmak için kaydedilmiş durum modülünün sizin için işleyebileceği bazı standart kodlar gerekir.
Bu modül kullanılırken ViewModel nesneleri, oluşturucusu aracılığıyla SavedStateHandle nesnesi alır. Bu nesne, kaydedilmiş duruma nesne yazmanıza ve nesne almanıza olanak tanıyan bir anahtar/değer çifti haritasıdır. Bu değerler, işlem sistem tarafından sonlandırıldıktan sonra da kalıcı olur ve aynı nesne üzerinden kullanılmaya devam eder.
Kaydedilen durum, görev yığınına bağlıdır. Görev yığınınız kaybolursa kaydedilen durumunuz da kaybolur. Bu durum, bir uygulama zorla durdurulduğunda, uygulamayı son kullanılanlar menüsünden kaldırdığınızda veya cihazı yeniden başlattığınızda ortaya çıkabilir. Bu gibi durumlarda görev yığını kaybolur ve kaydedilen durumdaki bilgileri geri yükleyemezsiniz. Kullanıcı tarafından başlatılan kullanıcı arayüzü durumunu kapatma senaryolarında, kaydedilen durum geri yüklenmez. Sistem tarafından başlatılan senaryolarda bu durum geçerlidir.
Kurulum
SavedStateHandle kullanmak için ViewModel oluşturucu bağımsız değişkeni olarak kabul edin.
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Daha sonra, ek yapılandırma yapmadan composable'larınızda ViewModel örneğini alabilirsiniz. Varsayılan ViewModel fabrikası, ViewModel için uygun SavedStateHandle değerini sağlar.
class MyViewModel : ViewModel() { /*...*/ } // import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( viewModel: MyViewModel = viewModel() ) { // use viewModel here }
Özel bir ViewModelProvider.Factory örneği sağlarken CreationExtras ve viewModelFactory DSL'yi kullanarak SavedStateHandle kullanımını etkinleştirebilirsiniz.
SavedStateHandle ile çalışma
SavedStateHandle sınıfı, set() ve get() yöntemleriyle kaydedilmiş durumdan veri yazmanıza ve veri almanıza olanak tanıyan bir anahtar/değer eşlemesidir.
SavedStateHandle kullanıldığında sorgu değeri, işlem sonlandığında korunur. Böylece, etkinlik veya parçanın bu değeri manuel olarak kaydetmesi, geri yüklemesi ve SavedStateHandle'ya geri iletmesi gerekmeden, kullanıcı yeniden oluşturmadan önce ve sonra aynı filtrelenmiş veri grubunu görür.ViewModel
SavedStateHandle ayrıca bir anahtar/değer çifti haritasıyla etkileşimde bulunurken bekleyebileceğiniz başka yöntemler de içerir:
contains(String key): Belirli bir anahtar için değer olup olmadığını kontrol eder.remove(String key): Belirli bir anahtarın değerini kaldırır.keys():SavedStateHandleiçinde bulunan tüm anahtarları döndürür.
Ayrıca, gözlemlenebilir bir veri tutucu kullanarak SavedStateHandle değerlerini de alabilirsiniz. Desteklenen türlerin listesinde şunlar yer alır:
StateFlow
SavedStateHandle hizmetinden StateFlow
observable içine sarılmış değerleri alabilirsiniz. Değeri doğrudan değiştirmeniz gerekip gerekmediğine bağlı olarak salt okunur veya değiştirilebilir bir akış seçebilirsiniz:
getStateFlow(): Yalnızca durumu okumanız gerekiyorsa bunu kullanın. Anahtarın değeriniSavedStateHandleiçinde başka bir yerde güncellediğinizde StateFlow yeni değeri alır. Bu, salt okunur bir akışı kullanıma sunmak ve akış operatörlerini kullanarak dönüştürmek istediğinizde idealdir.getMutableStateFlow(): Hem okuma hem de yazma erişimine ihtiyacınız varsa bunu kullanın. DöndürülenMutableStateFlowöğesinin.valuedeğerini güncellemek, temel alınanSavedStateHandleöğesini otomatik olarak günceller. Böylece anahtarı manuel olarak ayarlamanız gerekmez.
Bu değerleri en sık olarak, bir veri listesini filtrelemek için sorgu girme gibi kullanıcı etkileşimleri nedeniyle güncellersiniz.
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { // Use getMutableStateFlow to read and write the query directly private val _query = savedStateHandle.getMutableStateFlow("query", "") val query: StateFlow= _query.asStateFlow() // Use getStateFlow if you only need a read-only stream to react to changes val filteredData: StateFlow<List > = query.flatMapLatest { repository.getFilteredData(it) } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() ) fun setQuery(newQuery: String) { // Updating the MutableStateFlow automatically updates the SavedStateHandle _query.value = newQuery } }
KotlinX Serialization desteği
Karmaşık kullanıcı arayüzü durumu için KotlinX Serialization ile birlikte saved özellik temsilcisini kullanabilirsiniz. Bu temsilci, özel @Serializable
veri sınıflarını doğrudan SavedStateHandle içine kalıcı olarak yerleştirmenize olanak tanır. Bu, ViewModel'inizin durumunu işlem sonlandırma boyunca korur. Böylece Compose kullanıcı arayüzünüz, yeniden oluşturulduğunda durumunu sorunsuz bir şekilde geri yükleyebilir.
Bu işlevi kullanmak için veri sınıfınıza @Serializable ekleyin ve ViewModel'inizde saved
delegesini kullanın:
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel // Ensure you have the savedstate-ktx dependency import androidx.savedstate.serialization.saved import kotlinx.serialization.Serializable @Serializable data class UserFilterState( val searchQuery: String, val minAge: Int, val includeInactive: Boolean ) class FilterViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { // The state is automatically serialized to a Bundle on process death, // and deserialized upon recreation. var filterState by savedStateHandle.saved { UserFilterState(searchQuery = "", minAge = 18, includeInactive = false) } fun updateQuery(newQuery: String) { // Mutating the property automatically updates the underlying SavedStateHandle filterState = filterState.copy(searchQuery = newQuery) } }
Compose State desteği
Durumunuz KotlinX Serialization yerine Compose'un Saver API'lerini kullanıyorsa lifecycle-viewmodel-compose yapısı saveable temsilcisini sağlar. Bu sayede, SavedStateHandle ile Compose'un Saver arasında birlikte çalışabilirlik sağlanır. Böylece, rememberSaveable üzerinden özel bir Saver ile kaydedebileceğiniz tüm State, SavedStateHandle ile de kaydedilebilir.
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { var filteredData: List<String> by savedStateHandle.saveable { mutableStateOf(emptyList()) } fun setQuery(query: String) { withMutableSnapshot { filteredData += query } } }
Desteklenen türler
SavedStateHandle içinde tutulan veriler, uygulamanızın savedInstanceState geri kalanıyla birlikte Bundle olarak kaydedilir ve geri yüklenir.
Doğrudan desteklenen türler
Varsayılan olarak, aşağıdaki örnekte gösterildiği gibi set() ve get() işlevlerini SavedStateHandle üzerinde Bundle ile aynı veri türleri için çağırabilirsiniz:
| Tür/Sınıf desteği | Dizi desteği |
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+) |
Sınıf yukarıdaki listede yer alan sınıflardan birini genişletmiyorsa @Parcelize Kotlin ek açıklamasını ekleyerek veya Parcelable doğrudan uygulayarak sınıfı paketlenebilir hale getirmeyi düşünebilirsiniz.
Paketlenebilir olmayan sınıfları kaydetme
Bir sınıf Parcelable veya Serializable uygulamıyorsa ve bu arayüzlerden birini uygulayacak şekilde değiştirilemiyorsa bu sınıfın bir örneğini doğrudan SavedStateHandle içine kaydetmek mümkün değildir.
Lifecycle 2.3.0-alpha03'ten itibaren SavedStateHandle, setSavedStateProvider() yöntemini kullanarak nesnenizi Bundle olarak kaydetme ve geri yükleme mantığınızı sağlayarak herhangi bir nesneyi kaydetmenize olanak tanır.
SavedStateRegistry.SavedStateProvider, kaydetmek istediğiniz durumu içeren bir Bundle döndüren tek bir saveState() yöntemini tanımlayan bir arayüzdür. SavedStateHandle durumu kaydetmeye hazır olduğunda saveState() işlevini çağırarak SavedStateProvider içindeki Bundle değerini alır ve ilişkili anahtar için Bundle değerini kaydeder.
Kamera uygulamasından ACTION_IMAGE_CAPTURE intent'i aracılığıyla resim isteyen bir uygulamayı ele alalım. Bu uygulama, kameranın resmi depolayacağı yer için geçici bir dosya gönderir. TempFileViewModel, bu geçici dosyayı oluşturma mantığını kapsar.
class TempFileViewModel : ViewModel() { private var tempFile: File? = null fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Etkinliğin işlemi sonlandırılıp daha sonra geri yüklenirse geçici dosyanın kaybolmaması için TempFileViewModel, verilerini kalıcı hale getirmek üzere SavedStateHandle kullanabilir. TempFileViewModel uygulamasının verilerini kaydetmesine izin vermek için SavedStateProvider uygulayın ve ViewModel uygulamasının SavedStateHandle üzerinde sağlayıcı olarak ayarlayın:
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 } } }
Kullanıcı geri döndüğünde File verilerini geri yüklemek için temp_file
Bundle değerini SavedStateHandle konumundan alın. Bu, mutlak yolu içeren saveTempFile() tarafından sağlanan Bundle ile aynıdır. Daha sonra mutlak yol, yeni bir File oluşturmak için kullanılabilir.
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 } } }
Testlerde SavedStateHandle
SavedStateHandle bağımlılığını kullanan bir ViewModel işlevini test etmek için SavedStateHandle işlevinin yeni bir örneğini, gerektirdiği test değerleriyle oluşturun ve test ettiğiniz ViewModel örneğine iletin.
class MyViewModelTest { private lateinit var viewModel: MyViewModel @Before fun setup() { val savedState = SavedStateHandle(mapOf("someIdArg" to testId)) viewModel = MyViewModel(savedState = savedState) } }
Ek kaynaklar
ViewModel için Kayıtlı Durum modülü hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın.
Codelab uygulamaları
İçeriği görüntüleme
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Kullanıcı arayüzü durumlarını kaydetme
- Gözlemlenebilir veri nesneleriyle çalışma
- Bağımlılıklarla ViewModel oluşturma