ViewModel के लिए सेव किया गया स्टेटस मॉड्यूल यह Android Jetpack का हिस्सा है.
यूज़र इंटरफ़ेस (यूआई) की स्थितियां सेव करना में बताए गए मुताबिक, ViewModel
ऑब्जेक्ट, कॉन्फ़िगरेशन में होने वाले बदलावों को मैनेज कर सकते हैं. इसलिए, आपको रोटेशन या अन्य मामलों में स्थिति के बारे में चिंता करने की ज़रूरत नहीं है. हालांकि, अगर आपको सिस्टम से शुरू की गई प्रोसेस के बंद होने की समस्या को मैनेज करना है, तो बैकअप के तौर पर SavedStateHandle
एपीआई का इस्तेमाल किया जा सकता है.
आम तौर पर, यूज़र इंटरफ़ेस की स्थिति को ViewModel
ऑब्जेक्ट में सेव किया जाता है या उसका रेफ़रंस दिया जाता है, न कि गतिविधियों में. इसलिए, onSaveInstanceState()
या rememberSaveable
का इस्तेमाल करने के लिए, कुछ बोइलरप्लेट की ज़रूरत होती है. सेव की गई स्थिति का मॉड्यूल, यह काम आपके लिए कर सकता है.
इस मॉड्यूल का इस्तेमाल करने पर, ViewModel
ऑब्जेक्ट को उसके कॉन्स्ट्रक्टर के ज़रिए SavedStateHandle
ऑब्जेक्ट मिलता है. यह ऑब्जेक्ट, एक कुंजी-वैल्यू मैप है. इसकी मदद से, सेव किए गए स्टेटस में ऑब्जेक्ट को लिखा और वापस पाया जा सकता है. सिस्टम की ओर से प्रोसेस को बंद करने के बाद भी ये वैल्यू बनी रहती हैं और उसी ऑब्जेक्ट के ज़रिए उपलब्ध रहती हैं.
सेव की गई स्थिति, आपके टास्क स्टैक से जुड़ी होती है. अगर आपका टास्क स्टैक हट जाता है, तो सेव की गई स्थिति भी हट जाती है. ऐसा तब हो सकता है, जब किसी ऐप्लिकेशन को जबरदस्ती बंद किया जाए, उसे हाल ही के ऐप्लिकेशन मेन्यू से हटाया जाए या डिवाइस को रीबूट किया जाए. ऐसे मामलों में, टास्क स्टैक गायब हो जाता है और सेव की गई स्थिति में जानकारी को वापस नहीं लाया जा सकता. उपयोगकर्ता की ओर से यूज़र इंटरफ़ेस की स्थिति को खारिज करने की स्थितियों में, सेव की गई स्थिति को वापस नहीं लाया जाता. सिस्टम से शुरू होने वाली स्थितियों में, ऐसा होता है.
सेटअप
Fragment 1.2.0 या इसकी ट्रांज़िशन डिपेंडेंसी Activity 1.1.0 से, ViewModel
के लिए कॉन्स्ट्रक्टर आर्ग्युमेंट के तौर पर SavedStateHandle
को स्वीकार किया जा सकता है.
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
इसके बाद, किसी और कॉन्फ़िगरेशन के बिना ही, अपने ViewModel
का कोई इंस्टेंस वापस पाया जा सकता है. डिफ़ॉल्ट ViewModel
फ़ैक्ट्री, आपके ViewModel
के लिए सही
SavedStateHandle
उपलब्ध कराती है.
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); ... } ... }
कस्टम ViewModelProvider.Factory
इंस्टेंस देते समय, AbstractSavedStateViewModelFactory
को एक्सटेंड करके SavedStateHandle
का इस्तेमाल चालू किया जा सकता है.
SavedStateHandle के साथ काम करना
SavedStateHandle
क्लास एक कीवर्ड-वैल्यू मैप है. इसकी मदद से, set()
और get()
तरीकों से, सेव की गई स्थिति में डेटा को सेव और फिर से पाया जा सकता है.
SavedStateHandle
का इस्तेमाल करने पर, प्रोसेस के बंद होने के बाद भी क्वेरी वैल्यू बनी रहती है. इससे यह पक्का होता है कि उपयोगकर्ता को रीक्रिएशन से पहले और बाद में, फ़िल्टर किए गए डेटा का एक ही सेट दिखे. इसके लिए, गतिविधि या फ़्रैगमेंट को मैन्युअल रूप से सेव करने, वापस लाने, और उस वैल्यू को ViewModel
पर वापस भेजने की ज़रूरत नहीं होती.
SavedStateHandle
में ऐसे अन्य तरीके भी हैं जिनका इस्तेमाल करके, कीवर्ड-वैल्यू मैप के साथ इंटरैक्ट किया जा सकता है:
contains(String key)
- यह जांचता है कि दी गई कुंजी के लिए कोई वैल्यू है या नहीं.remove(String key)
- इससे, दी गई कुंजी की वैल्यू हट जाती है.keys()
-SavedStateHandle
में मौजूद सभी कुंजियों को दिखाता है.
इसके अलावा, SavedStateHandle
से वैल्यू पाने के लिए, डेटा होल्डर का इस्तेमाल किया जा सकता है. इन टाइप की फ़ाइलें इस्तेमाल की जा सकती हैं:
LiveData
SavedStateHandle
से ऐसी वैल्यू पाएं जो LiveData
में रैप की गई हैं. इन्हें getLiveData()
का इस्तेमाल करके देखा जा सकता है.
जब कुंजी की वैल्यू अपडेट होती है, तो LiveData
को नई वैल्यू मिलती है. ज़्यादातर मामलों में, वैल्यू उपयोगकर्ता के इंटरैक्शन की वजह से सेट होती है. जैसे, डेटा की सूची को फ़िल्टर करने के लिए क्वेरी डालना. इसके बाद, अपडेट की गई इस वैल्यू का इस्तेमाल, 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
SavedStateHandle
से ऐसी वैल्यू पाएं जो StateFlow
में रैप की गई हैं. इन वैल्यू को getStateFlow()
का इस्तेमाल करके देखा जा सकता है.
जब कुंजी की वैल्यू अपडेट की जाती है, तो StateFlow
को नई वैल्यू मिलती है. ज़्यादातर मामलों में, उपयोगकर्ता के इंटरैक्शन की वजह से वैल्यू सेट की जा सकती है. जैसे, डेटा की सूची को फ़िल्टर करने के लिए क्वेरी डालना. इसके बाद, अन्य फ़्लो ऑपरेटर का इस्तेमाल करके, अपडेट की गई इस वैल्यू को बदला जा सकता है.
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 } }
एक्सपेरिमेंट के तौर पर उपलब्ध Compose की सुविधा के स्टेटस के बारे में जानकारी
lifecycle-viewmodel-compose
आर्टफ़ैक्ट, एक्सपेरिमेंट के तौर पर उपलब्ध saveable
एपीआई उपलब्ध कराता है. इनकी मदद से, SavedStateHandle
और Compose के Saver
के बीच इंटरऑपरेबिलिटी की सुविधा मिलती है. इसकी मदद से, rememberSaveable
के ज़रिए कस्टम Saver
के साथ सेव किए जा सकने वाले किसी भी State
को 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 } } }
इस्तेमाल किए जा सकने वाले टाइप
SavedStateHandle
में सेव किया गया डेटा, ऐक्टिविटी या फ़्रैगमेंट के बाकी savedInstanceState
के साथ-साथ, Bundle
के तौर पर सेव और वापस लाया जाता है.
सीधे तौर पर काम करने वाले टाइप
डिफ़ॉल्ट रूप से, 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
कोटलिन एनोटेशन जोड़कर या सीधे Parcelable
को लागू करके, क्लास को पार्सल करने लायक बनाएं.
ऐसी क्लास सेव करना जिन्हें पार्सल नहीं किया जा सकता
अगर कोई क्लास Parcelable
या Serializable
को लागू नहीं करती है और उनमें से किसी एक इंटरफ़ेस को लागू करने के लिए उसमें बदलाव नहीं किया जा सकता, तो उस क्लास के इंस्टेंस को सीधे SavedStateHandle
में सेव नहीं किया जा सकता.
Lifecycle 2.3.0-alpha03 से, SavedStateHandle
की मदद से किसी भी ऑब्जेक्ट को सेव किया जा सकता है. इसके लिए, आपको setSavedStateProvider()
के तरीके का इस्तेमाल करके, अपने ऑब्जेक्ट को Bundle
के तौर पर सेव और वापस लाने के लिए, अपना लॉजिक देना होगा. SavedStateRegistry.SavedStateProvider
एक ऐसा इंटरफ़ेस है जो एक saveState()
तरीका तय करता है. यह तरीका, Bundle
दिखाता है, जिसमें वह स्टेट होती है जिसे सेव करना है. जब SavedStateHandle
अपनी स्थिति सेव करने के लिए तैयार हो जाता है, तो वह SavedStateProvider
से Bundle
को वापस पाने के लिए saveState()
को कॉल करता है. साथ ही, उससे जुड़ी कुंजी के लिए Bundle
को सेव करता है.
एक ऐसे ऐप्लिकेशन का उदाहरण लें जो ACTION_IMAGE_CAPTURE
इंटेंट के ज़रिए, कैमरा ऐप्लिकेशन से इमेज का अनुरोध करता है. साथ ही, कैमरे को इमेज को सेव करने के लिए, कुछ समय तक रहने वाली फ़ाइल पास करता है. TempFileViewModel
, उस अस्थायी फ़ाइल को बनाने के लिए लॉजिक को शामिल करता है.
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; } }
अगर गतिविधि की प्रोसेस को बंद कर दिया जाता है और बाद में उसे फिर से शुरू किया जाता है, तो यह पक्का करने के लिए कि अस्थायी फ़ाइल न मिटे, TempFileViewModel
अपने डेटा को सेव करने के लिए SavedStateHandle
का इस्तेमाल कर सकता है. TempFileViewModel
को अपना डेटा सेव करने की अनुमति देने के लिए, SavedStateProvider
को लागू करें और इसे ViewModel
के SavedStateHandle
पर, सेवा देने वाली कंपनी के तौर पर सेट करें:
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; } } }
जब उपयोगकर्ता वापस आए, तो File
का डेटा वापस पाने के लिए, SavedStateHandle
से temp_file
Bundle
को वापस पाएं. यह वही Bundle
है जो saveTempFile()
से मिलता है और जिसमें पूरा पाथ होता है. इसके बाद, ऐब्सलूट पाथ का इस्तेमाल करके, नए 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
SavedStateHandle
को डिपेंडेंसी के तौर पर इस्तेमाल करने वाले ViewModel
की जांच करने के लिए, SavedStateHandle
का एक नया इंस्टेंस बनाएं. इसमें, SavedStateHandle
के लिए ज़रूरी टेस्ट वैल्यू डालें और इसे उस ViewModel
इंस्टेंस को पास करें जिसकी जांच की जा रही है.
Kotlin
class MyViewModelTest { private lateinit var viewModel: MyViewModel @Before fun setup() { val savedState = SavedStateHandle(mapOf("someIdArg" to testId)) viewModel = MyViewModel(savedState = savedState) } }
अन्य संसाधन
ViewModel
के लिए सेव किए गए स्टेटस मॉड्यूल के बारे में ज़्यादा जानकारी के लिए, यहां दिए गए संसाधन देखें.
कोडलैब
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- यूज़र इंटरफ़ेस (यूआई) की स्थितियां सेव करना
- निगरानी किए जा सकने वाले डेटा ऑब्जेक्ट के साथ काम करना
- डिपेंडेंसी वाले ViewModel बनाना