ViewModel के लिए सेव की गई स्थिति वाला मॉड्यूल Android Jetpack का हिस्सा है.
यूज़र इंटरफ़ेस (यूआई) की स्थितियां सेव करना में बताया गया है कि ViewModel ऑब्जेक्ट, कॉन्फ़िगरेशन में होने वाले बदलावों को मैनेज कर सकते हैं. इसलिए, आपको रोटेशन या अन्य मामलों में स्थिति के बारे में चिंता करने की ज़रूरत नहीं है. हालांकि, अगर आपको सिस्टम की ओर से शुरू की गई प्रोसेस के बंद होने की समस्या को ठीक करना है, तो बैकअप के तौर पर SavedStateHandle एपीआई का इस्तेमाल किया जा सकता है.
यूज़र इंटरफ़ेस (यूआई) की स्थिति को आम तौर पर ViewModel ऑब्जेक्ट में सेव किया जाता है या उनका रेफ़रंस दिया जाता है. इसलिए, Compose में rememberSaveable का इस्तेमाल करने के लिए, कुछ बॉयलरप्लेट कोड की ज़रूरत होती है. इसे सेव किए गए स्टेट मॉड्यूल से मैनेज किया जा सकता है.
इस मॉड्यूल का इस्तेमाल करते समय, ViewModel ऑब्जेक्ट को कंस्ट्रक्टर के ज़रिए SavedStateHandle ऑब्जेक्ट मिलता है. यह ऑब्जेक्ट, कुंजी-वैल्यू मैप होता है. इसकी मदद से, सेव की गई स्थिति में ऑब्जेक्ट लिखे और वापस पाए जा सकते हैं. सिस्टम के प्रोसेस बंद करने के बाद भी ये वैल्यू बनी रहती हैं. साथ ही, ये उसी ऑब्जेक्ट के ज़रिए उपलब्ध रहती हैं.
सेव की गई स्थिति, आपके टास्क स्टैक से जुड़ी होती है. टास्क स्टैक बंद होने पर, सेव की गई स्थिति भी बंद हो जाती है. ऐसा किसी ऐप्लिकेशन को बंद करने, हाल ही के ऐप्लिकेशन मेन्यू से ऐप्लिकेशन को हटाने या डिवाइस को रीबूट करने पर हो सकता है. ऐसे मामलों में, टास्क स्टैक गायब हो जाता है. साथ ही, सेव की गई स्थिति में जानकारी को वापस नहीं लाया जा सकता. उपयोगकर्ता के यूज़र इंटरफ़ेस (यूआई) को बंद करने के मामलों में, सेव की गई स्थिति को वापस नहीं लाया जाता. सिस्टम की ओर से शुरू किए गए अनुरोधों के लिए, यह ज़रूरी है.
सेटअप
SavedStateHandle का इस्तेमाल करने के लिए, इसे अपने ViewModel के कंस्ट्रक्टर आर्ग्युमेंट के तौर पर स्वीकार करें.
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
इसके बाद, किसी भी अतिरिक्त कॉन्फ़िगरेशन के बिना, अपने कंपोज़ेबल में ViewModel का इंस्टेंस वापस पाया जा सकता है. डिफ़ॉल्ट ViewModel फ़ैक्ट्री, आपके ViewModel के लिए सही SavedStateHandle उपलब्ध कराती है.
class MyViewModel : ViewModel() { /*...*/ } // import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( viewModel: MyViewModel = viewModel() ) { // use viewModel here }
कस्टम ViewModelProvider.Factory इंस्टेंस देते समय, CreationExtras और viewModelFactory डीएसएल का इस्तेमाल करके, SavedStateHandle के इस्तेमाल को चालू किया जा सकता है.
SavedStateHandle का इस्तेमाल करना
SavedStateHandle क्लास एक कुंजी-वैल्यू मैप है. इसकी मदद से, set() और get() तरीकों का इस्तेमाल करके, सेव की गई स्थिति में डेटा लिखा और उससे डेटा वापस पाया जा सकता है.
SavedStateHandle का इस्तेमाल करने पर, प्रोसेस बंद होने के बाद भी क्वेरी वैल्यू बनी रहती है. इससे यह पक्का होता है कि उपयोगकर्ता को फ़िल्टर किया गया डेटा, फिर से बनाए जाने से पहले और बाद में एक जैसा दिखे. इसके लिए, गतिविधि या फ़्रैगमेंट को उस वैल्यू को मैन्युअल तरीके से सेव, वापस लाने, और ViewModel को वापस भेजने की ज़रूरत नहीं होती.
SavedStateHandle में ऐसे अन्य तरीके भी हैं जो आपको कुंजी-वैल्यू मैप के साथ इंटरैक्ट करते समय मिल सकते हैं:
contains(String key)- इससे यह पता चलता है कि दी गई कुंजी के लिए कोई वैल्यू मौजूद है या नहीं.remove(String key)- इससे दी गई कुंजी की वैल्यू हट जाती है.keys()- इससेSavedStateHandleमें मौजूद सभी कुंजियां दिखती हैं.
इसके अलावा, ऑब्ज़र्वेबल डेटा होल्डर का इस्तेमाल करके, SavedStateHandle से वैल्यू वापस पाई जा सकती हैं. इसके साथ काम करने वाले टाइप की सूची में ये शामिल हैं:
StateFlow
SavedStateHandle से वैल्यू को StateFlow
ऑब्ज़र्वेबल में रैप करके वापस पाया जा सकता है. अगर आपको वैल्यू में सीधे तौर पर बदलाव करना है, तो आपके पास सिर्फ़ पढ़ने के लिए या बदलाव करने के लिए स्ट्रीम चुनने का विकल्प होता है:
getStateFlow(): अगर आपको सिर्फ़ स्थिति पढ़नी है, तो इसका इस्तेमाल करें.SavedStateHandleमें किसी दूसरी जगह पर कुंजी की वैल्यू अपडेट करने पर, StateFlow को नई वैल्यू मिलती है. यह तब सबसे सही होता है, जब आपको सिर्फ़ पढ़ने वाली स्ट्रीम को दिखाना हो और उसे Flow ऑपरेटर का इस्तेमाल करके बदलना हो.getMutableStateFlow(): अगर आपको पढ़ने और लिखने, दोनों का ऐक्सेस चाहिए, तो इसका इस्तेमाल करें. जवाब में मिलेMutableStateFlowके.valueको अपडेट करने पर, उससे जुड़ाSavedStateHandleअपने-आप अपडेट हो जाता है. इससे आपको मैन्युअल तरीके से कुंजी सेट करने की ज़रूरत नहीं पड़ती.
आम तौर पर, उपयोगकर्ता के इंटरैक्शन की वजह से इन वैल्यू को अपडेट किया जाता है. जैसे, डेटा की सूची को फ़िल्टर करने के लिए क्वेरी डालना.
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 के साथ काम करता है
मुश्किल यूज़र इंटरफ़ेस (यूआई) की स्थिति के लिए, KotlinX Serialization के साथ-साथ saved प्रॉपर्टी डेलिगेट का इस्तेमाल किया जा सकता है. इस डेलिगेट की मदद से, कस्टम @Serializable डेटा क्लास को सीधे SavedStateHandle में सेव किया जा सकता है. इससे प्रोसेस बंद होने पर भी, आपके ViewModel की स्थिति बनी रहती है. इसलिए, Compose यूज़र इंटरफ़ेस (यूआई) को फिर से बनाने पर, उसकी स्थिति को आसानी से वापस लाया जा सकता है.
इसका इस्तेमाल करने के लिए, अपनी डेटा क्लास को @Serializable से एनोटेट करें. इसके बाद, अपने ViewModel में saved
delegate का इस्तेमाल करें:
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 में स्टेट को मैनेज करने की सुविधा
अगर आपका ऐप्लिकेशन, KotlinX Serialization के बजाय Compose के Saver एपीआई पर निर्भर करता है, तो lifecycle-viewmodel-compose आर्टफ़ैक्ट, saveable डेलिगेट उपलब्ध कराता है. इससे SavedStateHandle और Compose के Saver के बीच इंटरऑपरेबिलिटी की अनुमति मिलती है. इससे, कस्टम Saver के साथ rememberSaveable के ज़रिए सेव किए जा सकने वाले किसी भी State को SavedStateHandle के साथ भी सेव किया जा सकता है.
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { var filteredData: List<String> by savedStateHandle.saveable { mutableStateOf(emptyList()) } fun setQuery(query: String) { withMutableSnapshot { filteredData += query } } }
इस तरह के कोड काम करते हैं
SavedStateHandle में मौजूद डेटा को Bundle के तौर पर सेव किया जाता है और वापस लाया जाता है. साथ ही, आपके ऐप्लिकेशन के लिए बाकी savedInstanceState भी सेव किए जाते हैं और वापस लाए जाते हैं.
इन फ़ाइल टाइप का इस्तेमाल किया जा सकता है
डिफ़ॉल्ट रूप से, Bundle के लिए इस्तेमाल किए गए डेटा टाइप के लिए, SavedStateHandle पर set() और get() को कॉल किया जा सकता है. यहां दिए गए उदाहरण में, ऐसा ही किया गया है:
| टाइप/क्लास के हिसाब से सहायता | ऐरे के लिए सहायता |
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+) |
अगर क्लास, ऊपर दी गई सूची में से किसी भी क्लास को एक्सटेंड नहीं करती है, तो @Parcelize Kotlin एनोटेशन जोड़कर या Parcelable को सीधे तौर पर लागू करके, क्लास को पार्सल किया जा सकने वाला बनाएं.
पार्सल नहीं की जा सकने वाली क्लास सेव करना
अगर कोई क्लास Parcelable या Serializable को लागू नहीं करती है और उसे इनमें से किसी एक इंटरफ़ेस को लागू करने के लिए बदला नहीं जा सकता, तो उस क्लास के इंस्टेंस को सीधे तौर पर SavedStateHandle में सेव नहीं किया जा सकता.
Lifecycle 2.3.0-alpha03 से, SavedStateHandle की मदद से किसी भी ऑब्जेक्ट को सेव किया जा सकता है. इसके लिए, आपको ऑब्जेक्ट को Bundle के तौर पर सेव करने और उसे वापस लाने के लिए, अपना लॉजिक देना होगा. इसके लिए, setSavedStateProvider() तरीके का इस्तेमाल करें.
SavedStateRegistry.SavedStateProvider एक ऐसा इंटरफ़ेस है जो एक saveState() तरीके को तय करता है. यह तरीका, Bundle दिखाता है. इसमें वह स्टेटस होता है जिसे आपको सेव करना है. जब SavedStateHandle अपनी स्थिति को सेव करने के लिए तैयार होता है, तब वह saveState() को कॉल करता है, ताकि SavedStateProvider से Bundle को वापस पाया जा सके. इसके बाद, वह Bundle को उससे जुड़ी कुंजी के लिए सेव करता है.
मान लें कि कोई ऐप्लिकेशन, ACTION_IMAGE_CAPTURE इंटेंट के ज़रिए Camera ऐप्लिकेशन से इमेज का अनुरोध करता है. साथ ही, वह एक ऐसी अस्थायी फ़ाइल पास करता है जिसमें यह जानकारी होती है कि कैमरे को इमेज कहां सेव करनी चाहिए. TempFileViewModel में, उस अस्थायी फ़ाइल को बनाने का लॉजिक शामिल होता है.
class TempFileViewModel : ViewModel() { private var tempFile: File? = null fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
अगर गतिविधि की प्रोसेस बंद हो जाती है और बाद में उसे वापस चालू किया जाता है, तो यह पक्का करने के लिए कि अस्थायी फ़ाइल न मिटे, TempFileViewModel अपने डेटा को सेव करने के लिए SavedStateHandle का इस्तेमाल कर सकता है. TempFileViewModel को अपना डेटा सेव करने की अनुमति देने के लिए, SavedStateProvider लागू करें और इसे ViewModel के SavedStateHandle पर प्रोवाइडर के तौर पर सेट करें:
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 } } }
जब उपयोगकर्ता वापस आता है, तब File डेटा को वापस लाने के लिए, SavedStateHandle से temp_file
Bundle को वापस पाएं. यह वही Bundle है जो saveTempFile() ने दिया है. इसमें पूरा पाथ शामिल होता है. इसके बाद, ऐब्सलूट पाथ का इस्तेमाल करके, नया File इंस्टैंशिएट किया जा सकता है.
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 } } }
जांच में SavedStateHandle का इस्तेमाल करना
अगर आपको किसी ऐसे ViewModel की जांच करनी है जो SavedStateHandle पर निर्भर करता है, तो SavedStateHandle का एक नया इंस्टेंस बनाएं. इसमें टेस्ट के लिए ज़रूरी वैल्यू शामिल करें. इसके बाद, इसे उस ViewModel इंस्टेंस को पास करें जिसकी आपको जांच करनी है.
class MyViewModelTest { private lateinit var viewModel: MyViewModel @Before fun setup() { val savedState = SavedStateHandle(mapOf("someIdArg" to testId)) viewModel = MyViewModel(savedState = savedState) } }
अन्य संसाधन
ViewModel के लिए Saved State मॉड्यूल के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें.
कोडलैब
कॉन्टेंट देखता है
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक का टेक्स्ट दिखता है
- यूज़र इंटरफ़ेस (यूआई) की स्थितियां सेव करना
- ऑब्ज़र्वेबल डेटा ऑब्जेक्ट के साथ काम करना
- डिपेंडेंसी के साथ ViewModels बनाना