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 के लिए सेव किए गए स्टेटस मॉड्यूल के बारे में ज़्यादा जानकारी के लिए, यहां दिए गए संसाधन देखें.

कोडलैब