ViewModel için Kayıtlı 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, rotasyonlarda veya diğer durumlarda durum hakkında endişelenmenize gerek yoktur. Ancak sistem tarafından başlatılan işlem sonlandırma işlemini ele almanız gerekiyorsa yedek olarak SavedStateHandle
API'yi kullanabilirsiniz.
Kullanıcı arayüzü durumu genellikle etkinliklerde değil, ViewModel
nesnelerinde depolanır veya bu nesnelere referans verilir. Bu nedenle, onSaveInstanceState()
veya rememberSaveable
kullanmak için kayıtlı durum modülünün sizin için halledebileceği bazı şablonlar gerekir.
Bu modül kullanılırken ViewModel
nesneleri, yapıcıları aracılığıyla bir SavedStateHandle
nesnesi alır. Bu nesne, kayıtlı durumdaki nesneleri yazıp almanıza olanak tanıyan bir anahtar/değer haritası. Bu değerler, süreç sistem tarafından sonlandırıldıktan sonra da aynı nesneyle kullanılabilir durumda kalır.
Kayıtlı durum, görev grubunuza bağlıdır. Görev grubunuzu kaldırırsanız kaydedilen durumunuz da kaldırılır. Bu durum, bir uygulamayı zorla durdurduğunuzda, uygulamayı son uygulamalar menüsünden kaldırdığınızda veya cihazı yeniden başlattığınızda ortaya çıkabilir. Bu gibi durumlarda görev grubu kaybolur ve bilgileri kayıtlı durumda geri yükleyemezsiniz. Kullanıcı tarafından başlatılan kullanıcı arayüzü durumunun kapatılması senaryolarında, kayıtlı durum geri yüklenmez. Sistem tarafından başlatılan senaryolarda bu geçerlidir.
Kurulum
Fragment 1.2.0 veya onun geçişli bağımlılığı Activity 1.1.0'dan itibaren, ViewModel
için bir kurucu bağımsız değişkeni olarak SavedStateHandle
kabul edebilirsiniz.
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
Ardından, ek yapılandırma yapmadan ViewModel
örneğinizi alabilirsiniz. Varsayılan ViewModel
fabrikası, ViewModel
cihazınız için uygun SavedStateHandle
sağlar.
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); ... } ... }
Özel bir ViewModelProvider.Factory
örneği sağlarken AbstractSavedStateViewModelFactory
öğesini genişleterek SavedStateHandle
kullanımını etkinleştirebilirsiniz.
SavedStateHandle ile çalışma
SavedStateHandle
sınıfı, set()
ve get()
yöntemleri aracılığıyla kayıtlı durumdaki verileri yazmanıza ve bu verileri bu durumdan almanıza olanak tanıyan bir anahtar/değer haritası.
SavedStateHandle
kullanıldığında sorgu değeri, işlem sona erdiğinde korunur. Böylece kullanıcı, yeniden oluşturma işleminden önce ve sonra aynı filtrelenmiş veri grubunu görür. Bu durumda, etkinliğin veya parçanın bu değeri manuel olarak kaydetmesi, geri yüklemesi ve ViewModel
'e geri göndermesi gerekmez.
SavedStateHandle
, anahtar/değer haritalarıyla etkileşimde bulunurken kullanabileceğiniz başka yöntemlere de sahiptir:
contains(String key)
: Belirtilen anahtar için bir değer olup olmadığını kontrol eder.remove(String key)
: Belirtilen anahtarın değerini kaldırır.keys()
:SavedStateHandle
içinde bulunan tüm anahtarları döndürür.
Ayrıca, gözlemlenebilir bir veri tutucusu kullanarak SavedStateHandle
öğesinden değer alabilirsiniz. Desteklenen türlerin listesi:
LiveData
getLiveData()
kullanılarak SavedStateHandle
kaynağından LiveData
gözlemlenebilir bir değere sarmalanmış değerleri alın.
Anahtarın değeri güncellendiğinde LiveData
yeni değeri alır. Değer genellikle kullanıcı etkileşimleri nedeniyle belirlenir (ör. bir veri listesini filtrelemek için sorgu girme). Bu güncellenmiş değer daha sonra LiveData
değerini dönüştürmek için kullanılabilir.
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
getStateFlow()
kullanılarak SavedStateHandle
kaynağından StateFlow
gözlemlenebilir bir değere sarmalanmış değerleri alın.
Anahtarın değerini güncellediğinizde StateFlow
yeni değeri alır. Değeri genellikle kullanıcı etkileşimlerine göre ayarlayabilirsiniz (ör. bir veri listesini filtrelemek için sorgu girme). Ardından, diğer akış operatörlerini kullanarak bu güncellenmiş değeri dönüştürebilirsiniz.
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 } }
Deneysel Oluşturma'nın durum desteği
lifecycle-viewmodel-compose
yapı, SavedStateHandle
ile Compose'un Saver
arasında birlikte çalışabilirliğe olanak tanıyan deneysel saveable
API'lerini sağlar. Böylece, özel bir Saver
ile rememberSaveable
üzerinden kaydedebileceğiniz tüm State
'leri SavedStateHandle
ile de kaydedebilirsiniz.
Kotlin
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
Bir SavedStateHandle
içinde tutulan veriler, etkinlik veya parça için savedInstanceState
'in geri kalanıyla birlikte Bundle
olarak kaydedilir ve geri yüklenir.
Doğrudan desteklenen türler
Varsayılan olarak, Bundle
ile aynı veri türleri için SavedStateHandle
üzerinde set()
ve get()
'yi çağırabilirsiniz. Aşağıda gösterildiği gibi:
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 listedekilerden birini genişletmiyorsa @Parcelize
Kotlin ek açıklamasını ekleyerek veya Parcelable
doğrudan uygulayarak sınıfı paketlenebilir hale getirebilirsiniz.
Bölünemeyen sınıfları kaydetme
Bir sınıf Parcelable
veya Serializable
'ü uygulamazsa ve bu arayüzlerden birini uygulamak için değiştirilemezse söz konusu 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 Bundle
olarak kaydetme ve geri yükleme için kendi 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öntemi tanımlayan bir arayüzdür. SavedStateHandle
durumunu kaydetmeye hazır olduğunda SavedStateProvider
'dan Bundle
'yi almak için saveState()
'u çağırır ve ilişkili anahtar için Bundle
'yi kaydeder.
ACTION_IMAGE_CAPTURE
intent'i aracılığıyla kamera uygulamasından resim isteyen ve kameranın resmi nereye sakladığını belirten geçici bir dosya gönderen bir uygulama örneğini düşünün. TempFileViewModel
, bu geçici dosyayı oluşturma mantığını kapsar.
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; } }
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
bölümünde sağlayıcı olarak ayarlayın:
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; } } }
Kullanıcı geri geldiğinde File
verilerini geri yüklemek için SavedStateHandle
'tan temp_file
Bundle
'yi alın. Bu, saveTempFile()
tarafından sağlanan ve mutlak yolu içeren Bundle
ile aynıdır. Daha sonra mutlak yol, yeni bir File
örneği oluşturmak için kullanılabilir.
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; } } }
Testlerde SavedStateHandle
Bağımlılık olarak SavedStateHandle
alan bir ViewModel
'yi test etmek için, ihtiyaç duyduğu test değerlerini içeren yeni bir SavedStateHandle
örneği oluşturun ve test ettiğiniz ViewModel
örneğine iletin.
Kotlin
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 isterseniz aşağıdaki kaynaklara göz atın.
Codelab uygulamaları
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ık içeren ViewModel'ler oluşturma