ViewModel की खास जानकारी   Android Jetpack का हिस्सा.

Kotlin Multiplatform का इस्तेमाल करके देखें
Kotlin Multiplatform की मदद से, कारोबार के लॉजिक को अन्य प्लैटफ़ॉर्म के साथ शेयर किया जा सकता है. KMP में ViewModel को सेट अप करने और उसका इस्तेमाल करने का तरीका जानें

ViewModel क्लास, कारोबार के लॉजिक या स्क्रीन लेवल के स्टेट होल्डर के तौर पर काम करती है. यह यूज़र इंटरफ़ेस (यूआई) को स्थिति दिखाता है और इससे जुड़े कारोबारी नियमों को शामिल करता है. इसका मुख्य फ़ायदा यह है कि यह स्थिति को कैश मेमोरी में सेव करता है और कॉन्फ़िगरेशन में बदलाव होने पर भी उसे बनाए रखता है. इसका मतलब है कि गतिविधियों के बीच नेविगेट करते समय या कॉन्फ़िगरेशन में बदलाव करने के बाद, आपके यूज़र इंटरफ़ेस (यूआई) को डेटा फिर से फ़ेच नहीं करना पड़ता. जैसे, स्क्रीन घुमाते समय.

स्टेट होल्डर के बारे में ज़्यादा जानने के लिए, स्टेट होल्डर से जुड़े दिशा-निर्देश देखें. इसी तरह, यूज़र इंटरफ़ेस (यूआई) लेयर के बारे में ज़्यादा जानने के लिए, यूज़र इंटरफ़ेस (यूआई) लेयर से जुड़े दिशा-निर्देश देखें.

ViewModel के फ़ायदे

ViewModel के बजाय, एक सामान्य क्लास का इस्तेमाल किया जा सकता है. इसमें वह डेटा होता है जिसे आपको अपने यूज़र इंटरफ़ेस (यूआई) में दिखाना है. इससे, एक ऐक्टिविटी से दूसरी ऐक्टिविटी या नेविगेशन डेस्टिनेशन के बीच नेविगेट करते समय समस्या हो सकती है. ऐसा करने से वह डेटा मिट जाता है. हालांकि, अगर आपने सेव की गई इंस्टेंस की स्थिति के तरीके का इस्तेमाल करके डेटा सेव किया है, तो वह मिटेगा नहीं. ViewModel, डेटा को सेव करने के लिए एक आसान एपीआई उपलब्ध कराता है. इससे यह समस्या हल हो जाती है.

इसके अलावा, सिर्फ़ स्टेट होल्डर के लिए Compose, retain सुविधाएं देता है. इनकी मदद से, सामान्य क्लास को ViewModel के पूरे इन्फ़्रास्ट्रक्चर के बिना, कॉन्फ़िगरेशन में होने वाले बदलावों के दौरान सुरक्षित रखा जा सकता है. दोनों ही तरीके, स्टेट को बनाए रखने में मदद करते हैं. हालांकि, आम तौर पर यह ज़्यादा सुरक्षित होता है कि बनाए रखे गए इंस्टेंस को ViewModel दिया जाए. इसके उलट, ViewModel को बनाए रखा गया इंस्टेंस देना ज़्यादा सुरक्षित नहीं होता, क्योंकि इनके लाइफ़साइकल और क्लीनअप के तरीके अलग-अलग होते हैं.

ViewModel क्लास के मुख्य फ़ायदे ये हैं:

  • इससे यूज़र इंटरफ़ेस (यूआई) की स्थिति को बनाए रखा जा सकता है.
  • यह कारोबारी नियमों का ऐक्सेस देता है.

स्थायी

ViewModel, दो तरीकों से डेटा को बनाए रखने की अनुमति देता है. पहला, ViewModel में मौजूद स्थिति के ज़रिए और दूसरा, ViewModel से ट्रिगर होने वाले ऑपरेशनों के ज़रिए. कैशिंग का मतलब है कि आपको स्क्रीन रोटेशन जैसे सामान्य कॉन्फ़िगरेशन में बदलाव करके, डेटा को फिर से फ़ेच नहीं करना पड़ता.

दायरा

ViewModel को इंस्टैंशिएट करते समय, आपको उसे ऐसा ऑब्जेक्ट पास करना होता है जो ViewModelStoreOwner इंटरफ़ेस लागू करता हो. यह नेविगेशन डेस्टिनेशन, नेविगेशन ग्राफ़, ऐक्टिविटी या कोई अन्य टाइप हो सकता है, जो इंटरफ़ेस लागू करता है. rememberViewModelStoreOwner एपीआई का इस्तेमाल करके, ViewModel को सीधे तौर पर किसी कंपोज़ेबल के स्कोप में भी रखा जा सकता है. इसके बाद, आपका ViewModel, ViewModelStoreOwner के Lifecycle के दायरे में आ जाता है. यह मेमोरी में तब तक सेव रहता है, जब तक इसका ViewModelStoreOwner हमेशा के लिए मिट नहीं जाता. जैसे, जब कंपोज़ेबल का मालिक कंपोज़िशन से बाहर निकल जाता है.

कई क्लास, ViewModelStoreOwner इंटरफ़ेस की डायरेक्ट या इनडायरेक्ट सबक्लास होती हैं. इस क्लास से सीधे इनहेरिट करने वाली सबक्लास ये हैं: ComponentActivity और NavBackStackEntry. इनडायरेक्ट सबक्लास की पूरी सूची देखने के लिए, ViewModelStoreOwner रेफ़रंस देखें. LazyList या Pager में मौजूद अलग-अलग आइटम के लिए ViewModels को स्कोप करने के लिए, rememberViewModelStoreProvider() का इस्तेमाल करके, पैरंट को मालिक के तौर पर सेट करें.

जब होस्ट ऐक्टिविटी के कॉन्फ़िगरेशन में बदलाव होता है, तब ViewModel में एसिंक्रोनस काम जारी रहता है. भले ही, इसे ऐक्टिविटी या किसी खास कंपोज़ेबल के स्कोप में रखा गया हो. यह लगातार बने रहने की कुंजी है.

ज़्यादा जानकारी के लिए, यहां दिया गया ViewModel का लाइफ़साइकल सेक्शन, ViewModel स्कोपिंग एपीआई, और Jetpack Compose के लिए स्टेट होस्टिंग से जुड़ी गाइड देखें.

SavedStateHandle

SavedStateHandle की मदद से, डेटा को सिर्फ़ कॉन्फ़िगरेशन में बदलाव होने पर ही नहीं, बल्कि प्रोसेस बंद होने पर भी सेव किया जा सकता है. इसका मतलब है कि यह आपको यूज़र इंटरफ़ेस (यूआई) की स्थिति को बरकरार रखने की सुविधा देता है. ऐसा तब भी होता है, जब उपयोगकर्ता ऐप्लिकेशन को बंद कर देता है और बाद में उसे खोलता है.

यूज़र इंटरफ़ेस (यूआई) की स्थिति को सेव करने के बारे में ज़्यादा जानने के लिए, Compose में यूज़र इंटरफ़ेस (यूआई) की स्थिति सेव करना लेख पढ़ें.

कारोबारी नियमों का ऐक्सेस

ज़्यादातर कारोबारी नियम डेटा लेयर में मौजूद होते हैं. हालांकि, यूज़र इंटरफ़ेस (यूआई) लेयर में भी कारोबारी नियम हो सकते हैं. ऐसा तब हो सकता है, जब स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति बनाने के लिए, कई रिपॉज़िटरी से डेटा को एक साथ जोड़ा जा रहा हो या जब किसी खास तरह के डेटा के लिए डेटा लेयर की ज़रूरत न हो.

यूज़र इंटरफ़ेस (यूआई) लेयर में कारोबारी लॉजिक को हैंडल करने के लिए, ViewModel सही जगह है. ViewModel, इवेंट को मैनेज करने और उन्हें हाइरार्की की अन्य लेयर को सौंपने का काम भी करता है. ऐसा तब होता है, जब ऐप्लिकेशन डेटा में बदलाव करने के लिए कारोबारी लॉजिक लागू करना होता है.

ViewModel लागू करना

यहां एक ऐसी स्क्रीन के लिए ViewModel को लागू करने का उदाहरण दिया गया है जिसमें उपयोगकर्ता को डाइस रोल करने की सुविधा मिलती है.

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // Expose screen UI state
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Handle business logic
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

इसके बाद, स्क्रीन-लेवल के कंपोज़ेबल से ViewModel को इस तरह ऐक्सेस किया जा सकता है:

import androidx.lifecycle.viewmodel.compose.viewModel

// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
    viewModel: DiceRollViewModel = viewModel()
) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    // Update UI elements
}

ViewModel के साथ कोरूटीन का इस्तेमाल करना

ViewModel में Kotlin कोरूटीन के लिए सहायता शामिल है. यह एसिंक्रोनस टास्क को उसी तरह से सेव कर सकता है जिस तरह से यूज़र इंटरफ़ेस (यूआई) की स्थिति को सेव करता है.

ज़्यादा जानकारी के लिए, Android के आर्किटेक्चर कॉम्पोनेंट के साथ Kotlin कोरूटीन इस्तेमाल करना लेख पढ़ें.

ViewModel की लाइफ़साइकल

ViewModel का लाइफ़साइकल, सीधे तौर पर उसके स्कोप से जुड़ा होता है. ViewModel, मेमोरी में तब तक सेव रहता है, जब तक कि वह ViewModelStoreOwner मिट न जाए जिसके स्कोप में वह आता है. ऐसा इन मामलों में हो सकता है:

  • किसी गतिविधि के खत्म होने पर.
  • नेविगेशन एंट्री के मामले में, जब इसे बैक स्टैक से हटा दिया जाता है.
  • कंपोज़ेबल के मामले में, जब यह कंपोज़िशन से बाहर निकल जाता है. ViewModel को सीधे तौर पर अपने यूज़र इंटरफ़ेस (यूआई) के किसी भी हिस्से (जैसे कि Pager या LazyList) से स्कोप करने के लिए, rememberViewModelStoreOwner का इस्तेमाल किया जा सकता है.

इसलिए, ViewModels, कॉन्फ़िगरेशन में बदलाव होने पर भी डेटा को सुरक्षित रखने का एक बेहतरीन तरीका है.

पहली इमेज में, किसी ऐक्टिविटी की अलग-अलग लाइफ़साइकल स्थितियां दिखाई गई हैं. इसमें दिखाया गया है कि रोटेशन के दौरान और रोटेशन के बाद ऐक्टिविटी की स्थिति क्या होती है. इस इलस्ट्रेशन में, ViewModel का लाइफ़टाइम भी दिखाया गया है. यह लाइफ़टाइम, उससे जुड़ी ऐक्टिविटी के लाइफ़साइकल के बगल में दिखाया गया है. इस डायग्राम में, किसी गतिविधि की अलग-अलग स्थितियां दिखाई गई हैं.

इस इमेज में, ऐक्टिविटी की स्थिति बदलने पर ViewModel की लाइफ़साइकल को दिखाया गया है.
पहली इमेज. किसी गतिविधि और ViewModel की लाइफ़साइकल की स्थितियां.

आम तौर पर, सिस्टम किसी गतिविधि ऑब्जेक्ट के onCreate() तरीके को पहली बार कॉल करने पर, ViewModel का अनुरोध किया जाता है. सिस्टम, किसी गतिविधि के दौरान onCreate() को कई बार कॉल कर सकता है. जैसे, जब किसी डिवाइस की स्क्रीन घुमाई जाती है. ViewModel तब तक मौजूद रहता है, जब तक ViewModel का पहला अनुरोध किया जाता है. यह तब तक मौजूद रहता है, जब तक गतिविधि पूरी नहीं हो जाती और उसे मिटा नहीं दिया जाता.

ViewModel की डिपेंडेंसी हटाना

ViewModel, onCleared तरीके को तब कॉल करता है, जब ViewModelStoreOwner इसे अपनी लाइफ़साइकल के दौरान बंद कर देता है. इससे आपको ViewModel के लाइफ़साइकल के हिसाब से, किसी भी काम या डिपेंडेंसी को हटाने में मदद मिलती है.

यहां दिए गए उदाहरण में, viewModelScope के विकल्प के तौर पर इस्तेमाल किए जा सकने वाले फ़ंक्शन के बारे में बताया गया है. viewModelScope एक बिल्ट-इन CoroutineScope है, जो ViewModel की लाइफ़साइकल को अपने-आप फ़ॉलो करता है. ViewModel इसका इस्तेमाल, कारोबार से जुड़े ऑपरेशनों को ट्रिगर करने के लिए करता है. अगर आपको आसानी से टेस्टिंग करने के लिए, viewModelScope के बजाय कस्टम स्कोप का इस्तेमाल करना है, तो ViewModel अपने कंस्ट्रक्टर में CoroutineScope को डिपेंडेंसी के तौर पर पा सकता है. जब ViewModelStoreOwner अपनी लाइफ़साइकल के आखिर में ViewModel को हटाता है, तो ViewModel भी CoroutineScope को रद्द कर देता है.

class MyViewModel(
    private val coroutineScope: CoroutineScope =
        CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {

    // Other ViewModel logic ...

    override fun onCleared() {
        coroutineScope.cancel()
    }
}

लाइफ़साइकल वर्शन 2.5 और इसके बाद के वर्शन में, ViewModel के कंस्ट्रक्टर को एक या उससे ज़्यादा Closeable ऑब्जेक्ट पास किए जा सकते हैं. ViewModel इंस्टेंस के बंद होने पर, ये ऑब्जेक्ट अपने-आप बंद हो जाते हैं.

class CloseableCoroutineScope(
    context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context
    override fun close() {
        coroutineContext.cancel()
   }
}

class MyViewModel(
    private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
    // Other ViewModel logic ...
}

सबसे सही तरीके

ViewModel को लागू करते समय, आपको यहां दिए गए सबसे सही तरीकों का पालन करना चाहिए:

  • स्कोपिंग की वजह से, ViewModels का इस्तेमाल स्क्रीन लेवल के स्टेट होल्डर की जानकारी को लागू करने के लिए करें. इनका इस्तेमाल, बार-बार इस्तेमाल किए जा सकने वाले यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट के स्टेट होल्डर के तौर पर न करें. जैसे, चिप ग्रुप या फ़ॉर्म. इसके अलावा, आपको एक ही ViewModelStoreOwner के तहत, एक ही यूआई कॉम्पोनेंट के अलग-अलग इस्तेमाल में, एक ही ViewModel इंस्टेंस मिलेगा. ऐसा तब तक होगा, जब तक कि हर चिप के लिए व्यू मॉडल की का इस्तेमाल नहीं किया जाता.
  • ViewModel को यूज़र इंटरफ़ेस (यूआई) लागू करने से जुड़ी जानकारी नहीं होनी चाहिए. ViewModel API के ज़रिए दिखाए जाने वाले तरीकों और यूज़र इंटरफ़ेस (यूआई) की स्थिति वाले फ़ील्ड के नाम, ज़्यादा से ज़्यादा सामान्य रखें. इस तरह, आपका ViewModel किसी भी तरह के यूज़र इंटरफ़ेस (यूआई) के साथ काम कर सकता है: मोबाइल फ़ोन, फ़ोल्ड किए जा सकने वाले डिवाइस, टैबलेट या Chromebook!
  • ViewModelStoreOwner से ज़्यादा समय तक मेमोरी में सेव रहने की वजह से, ViewModels को लाइफ़साइकल से जुड़े एपीआई के किसी भी रेफ़रंस को सेव नहीं करना चाहिए. जैसे, Context या Resources. इससे मेमोरी लीक होने से रोका जा सकता है.
  • ViewModels को अन्य क्लास, फ़ंक्शन या अन्य यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट में पास न करें. इनका मैनेजमेंट प्लैटफ़ॉर्म करता है. इसलिए, आपको इन्हें प्लैटफ़ॉर्म के जितना हो सके उतना करीब रखना चाहिए. जैसे, अपनी गतिविधि, स्क्रीन लेवल कंपोज़ेबल फ़ंक्शन या नेविगेशन डेस्टिनेशन के करीब. इससे निचले लेवल के कॉम्पोनेंट को ज़रूरत से ज़्यादा डेटा और लॉजिक ऐक्सेस करने से रोका जाता है.

ज़्यादा जानकारी

डेटा ज़्यादा जटिल होने पर, सिर्फ़ डेटा लोड करने के लिए अलग क्लास बनाई जा सकती है. ViewModel का मकसद, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर के लिए डेटा को इनकैप्सुलेट करना है, ताकि कॉन्फ़िगरेशन में बदलाव होने पर भी डेटा सुरक्षित रहे. कॉन्फ़िगरेशन में बदलाव होने पर, डेटा को लोड करने, सेव करने, और मैनेज करने के बारे में जानकारी पाने के लिए, सेव की गई यूज़र इंटरफ़ेस स्थितियां देखें.

Android ऐप्लिकेशन के आर्किटेक्चर की गाइड में, इन फ़ंक्शन को मैनेज करने के लिए रिपॉज़िटरी क्लास बनाने का सुझाव दिया गया है.

अन्य संसाधन

ViewModel क्लास के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें.

दस्तावेज़

कॉन्टेंट देखता है

सैंपल