viewModelScope androidx.lifecycle लाइब्रेरी से एक अंतर्निहित CoroutineScope है जो ViewModel के जीवनचक्र से जुड़ा होता है और इसके साफ़ होने पर स्वचालित रूप से रद्द हो जाता है। Google Android Developers, 2025 के अनुसार, viewModelScope MVVM आर्किटेक्चर में कोरूटीन लॉन्च करने का मानक तंत्र है, जो मेमोरी लीक के जोखिम के बिना सुरक्षित एसिंक्रोनस संचालन सुनिश्चित करता है। ViewModelScope डिफ़ॉल्ट रूप से Dispatchers.Main का उपयोग करता है, और इसके अंदर सभी IO संचालन withContext के माध्यम से निष्पादित होने चाहिए।
मुख्य बातें
viewModelScope ViewModel इंटरफ़ेस पर एक extension प्रॉपर्टी है, जिसे lifecycle-viewmodel-ktx लाइब्रेरी (संस्करण 2.1.0 से शुरू) में जोड़ा गया है। यह ViewModel जीवनचक्र से जुड़ा एक तैयार CoroutineScope प्रदान करता है।
// Internal structure (simplified)
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope = this.getTag(JOB_KEY)
if (scope != null) return scope
return CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
.also { setTag(JOB_KEY, it) }
}
Scope पहली बार एक्सेस करने पर लेज़ी (lazy) रूप से बनाया जाता है और setTag के माध्यम से कैश किया जाता है। यह SupervisorJob का उपयोग करता है, जिसका अर्थ है कि एक चाइल्ड कोरूटीन में अपवाद दूसरों को रद्द नहीं करता। डिफ़ॉल्ट डिस्पैचर Dispatchers.Main.immediate है, जो पहले से Main पर होने पर अतिरिक्त डिस्पैचिंग के बिना मुख्य थ्रेड पर कोड निष्पादित करता है।
जब ViewModel जीवनचक्र छोड़ती है (Activity समाप्त होती है या Fragment हटा दिया जाता है), सिस्टम clear() को कॉल करता है, जो onCleared() को ट्रिगर करता है। इस कॉलबैक में, viewModelScope अपने Job को रद्द करता है, जो सभी सक्रिय कोरूटीन को पुनरावर्ती रूप से समाप्त करता है। तंत्र Closeable इंटरफ़ेस के माध्यम से कार्यान्वित किया गया है, जहाँ scope Job स्वचालित बंद करने के लिए एक संसाधन के रूप में पंजीकृत होता है।
viewModelScope का ViewModel जीवनचक्र से जुड़ाव तंत्र टैगिंग और onCleared कॉलबैक पर आधारित है। आइए इसे चरण दर चरण समझते हैं।
जब ViewModel viewModelScope.launch { ... } निष्पादित करता है, getter जाँचता है कि JOB_KEY टैग के तहत पहले से कोई scope संग्रहीत है या नहीं। यदि scope मौजूद नहीं है, तो एक नया CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) इंस्टेंस बनाया जाता है। Scope आंतरिक टैग मैप के माध्यम से ViewModel के अंदर संग्रहीत होता है।
viewModelScope.launch या viewModelScope.async के माध्यम से लॉन्च किए गए सभी कोरूटीन scope के SupervisorJob के चाइल्ड बन जाते हैं। वे मुख्य थ्रेड पर चलते हैं (जब तक withContext के माध्यम से कोई भिन्न डिस्पैचर निर्दिष्ट न किया गया हो)। जब तक ViewModel जीवित है, कोरूटीन सक्रिय, निलंबित या पूर्ण हो सकते हैं।
जब सिस्टम ViewModel को नष्ट करता है, ViewModel.clear() कॉल किया जाता है। clear() के अंदर, निम्नलिखित होता है:
जब स्क्रीन घुमाई जाती है, Activity पुनः बनाई जाती है, लेकिन ViewModel बनी रहती है (ViewModelStoreOwner के कारण)। इसका मतलब है कि viewModelScope सक्रिय रहता है और कोरूटीन बिना किसी रुकावट के निष्पादित होते रहते हैं। Activity के पुनः बनने के बाद, उसी ViewModel (और उसी scope) का पुनः उपयोग किया जाता है — डेटा लोडिंग शुरू से नहीं होती।
MVVM (Model-View-ViewModel) Android अनुप्रयोगों के लिए Google द्वारा अनुशंसित आर्किटेक्चर है। viewModelScope इसमें एसिंक्रोनस संचालन के निष्पादक के रूप में केंद्रीय स्थान रखता है।
| परत | घटक | viewModelScope की भूमिका |
|---|---|---|
| UI | Activity / Fragment | ViewModel से StateFlow/LiveData का अवलोकन करता है |
| ViewModel | ViewModel | viewModelScope के माध्यम से कोरूटीन लॉन्च करता है, UI स्थिति प्रबंधित करता है |
| Repository | Repository | viewModelScope कोरूटीन से कॉल की जाने वाली suspend फ़ंक्शन प्रदान करता है |
| Data | DAO / Api | वास्तविक अनुरोध निष्पादित करता है (Room, Retrofit) |
ViewModel viewModelScope के माध्यम से कोरूटीन लॉन्च करता है, जिसके अंदर यह Repository की suspend फ़ंक्शन को कॉल करता है। परिणाम StateFlow में रूपांतरित होता है, जिसे UI परत देखती है। यह डिज़ाइन जिम्मेदारियों का स्पष्ट विभाजन और प्रत्येक परत की स्वतंत्र परीक्षण क्षमता सुनिश्चित करता है।
यदि कोरूटीन Fragment से लॉन्च किए जाते, तो स्क्रीन घुमाने पर Fragment के नष्ट होने के साथ वे रद्द हो जाते। ViewModel रोटेशन से बच जाती है, इसलिए इसके scope में लॉन्च किए गए कोरूटीन निष्पादित होते रहते हैं। डेटा लोड करते समय lifecycleScope की तुलना में viewModelScope का यह मुख्य लाभ है।
आइए Kotlin में Android अनुप्रयोग में viewModelScope के उपयोग के तीन व्यावहारिक परिदृश्य देखें।
class ProfileViewModel(
private val repo: ProfileRepository
) : ViewModel() {
private val _profile = MutableStateFlow<Profile?>(null)
val profile: StateFlow<Profile?> = _profile
init {
loadProfile()
}
private fun loadProfile() {
viewModelScope.launch {
val result = repo.getProfile()
_profile.value = result
}
}
}
init ब्लॉक में, प्रोफ़ाइल लोडिंग तुरंत शुरू होती है। कोरूटीन मुख्य थ्रेड पर (डिफ़ॉल्ट रूप से) चलता है। रिपॉज़िटरी अपने suspend फ़ंक्शन के अंदर नेटवर्क अनुरोध के लिए withContext(Dispatchers.IO) का उपयोग करता है, इसलिए ViewModel को थ्रेड स्विचिंग की चिंता नहीं करनी पड़ती।
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<Item>) : UiState()
data class Error(val message: String) : UiState()
}
fun fetchItems() {
_state.value = UiState.Loading
viewModelScope.launch {
try {
val items = repo.getItems()
_state.value = UiState.Success(items)
} catch (e: Exception) {
_state.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
UI स्थिति को sealed class UiState के माध्यम से वर्णित किया गया है। ViewModel प्रत्येक परिवर्तन पर स्थिति अपडेट करता है। Fragment StateFlow की सदस्यता लेता है और केवल वर्तमान स्थिति पर प्रतिक्रिया करता है, पिछले रोटेशन की पुरानी कॉल को अनदेखा करता है।
private var searchJob: Job? = null
fun search(query: String) {
searchJob?.cancel()
searchJob = viewModelScope.launch {
delay(300)
val results = repo.search(query)
_searchResults.value = results
}
}
प्रत्येक नई खोज क्वेरी पर, पिछला कोरूटीन रद्द हो जाता है। delay(300) debounce लागू करता है — खोज केवल 300 मिलीसेकंड की निष्क्रियता के बाद निष्पादित होती है। यह सर्वर लोड को कम करता है और पुराने परिणामों को रोकता है।
दोनों scope AndroidX Lifecycle लाइब्रेरी द्वारा प्रदान किए गए हैं, लेकिन विभिन्न जीवनचक्रों से जुड़े हैं। चुनाव कार्य के प्रकार पर निर्भर करता है।
| विशेषता | viewModelScope | lifecycleScope |
|---|---|---|
| स्वामी | ViewModel | LifecycleOwner (Activity/Fragment) |
| रोटेशन पर रद्द | नहीं (ViewModel बच जाती है) | हाँ (Activity पुनः बनाई जाती है) |
| डिफ़ॉल्ट डिस्पैचर | Dispatchers.Main.immediate | Dispatchers.Main.immediate |
| उपलब्ध | ViewModel | Activity, Fragment, Service |
| सामान्य उपयोग | डेटा लोडिंग, व्यावसायिक तर्क | UI इंटरैक्शन, एनिमेशन |
Google सभी डेटा लोडिंग और प्रसंस्करण कार्यों के लिए viewModelScope का उपयोग करने की अनुशंसा करता है। lifecycleScope का उपयोग UI जीवनचक्र के किसी विशिष्ट क्षण से जुड़े संचालन के लिए किया जाना चाहिए — उदाहरण के लिए, पहली स्क्रीन उपस्थिति पर एनिमेशन शुरू करना या लोकेशन अपडेट की सदस्यता लेना जो स्क्रीन छोड़ने पर बंद हो जानी चाहिए।
एक अच्छी तरह से प्रलेखित Android API में भी, डेवलपर्स विशिष्ट गलतियाँ करते हैं। आइए चार सबसे सामान्य समस्याओं को देखें।
सबसे खतरनाक गलती ViewModel के साफ़ होने के बाद StateFlow या LiveData को अपडेट करने का प्रयास करना है। हालाँकि viewModelScope onCleared() पर रद्द हो जाता है, एक कोरूटीन वास्तविक रद्दीकरण प्रभावी होने से पहले कोड निष्पादित कर सकता है। जाँच के लिए isActive का उपयोग करें या catch-ब्लॉक के पूरा होने पर भरोसा करें।
viewModelScope आंतरिक रूप से SupervisorJob का उपयोग करता है, जो कोरूटीन के बीच त्रुटियों को अलग करता है। हालाँकि, यदि आप viewModelScope.launch के अंदर अपने स्वयं के Job() के साथ कोरूटीन लॉन्च करते हैं, तो वह कोरूटीन SupervisorJob का चाइल्ड बन जाता है लेकिन अन्य कोरूटीन में त्रुटियों के कारण रद्दीकरण से सुरक्षित नहीं रहेगा।
हालाँकि viewModelScope की कोई सख्त सीमा नहीं है, हजारों सक्रिय कोरूटीन सिस्टम को धीमा कर सकते हैं। लंबी डेटा सूचियों के लिए, प्रत्येक आइटम के लिए अलग कोरूटीन बनाने के बजाय Flow का collectLatest के साथ उपयोग करें।
यदि viewModelScope के बजाय गलती से GlobalScope आयात किया जाता है, तो ViewModel के साफ़ होने पर कोरूटीन रद्द नहीं होगा। इससे मेमोरी लीक और संभावित क्रैश होते हैं। हमेशा सुनिश्चित करें कि कोरूटीन viewModelScope के माध्यम से लॉन्च किए गए हैं, विशेष रूप से Fragment उपवर्गों में।
अक्सर पूछे जाने वाले प्रश्न
आप viewModelScope के डिस्पैचर को सीधे नहीं बदल सकते — यह Dispatchers.Main.immediate के रूप में हार्डकोड है। हालाँकि, कोरूटीन के अंदर आप withContext के माध्यम से दूसरे डिस्पैचर पर स्विच कर सकते हैं। परीक्षणों में डिस्पैचर बदलने के लिए, Rule के माध्यम से TestDispatcher का उपयोग करें।
scope को Repository में न पास करें — यह आर्किटेक्चर सिद्धांतों का उल्लंघन करता है। Repository को suspend फ़ंक्शन प्रदान करने चाहिए, और ViewModel स्वयं viewModelScope के माध्यम से कोरूटीन प्रबंधित करता है। यदि Repository को scope की आवश्यकता है, तो Clean Architecture के पक्ष में आर्किटेक्चर पर पुनर्विचार करें।
SupervisorJob सुनिश्चित करता है कि एक कोरूटीन में अपवाद (उदाहरण के लिए, कई स्वतंत्र अनुरोधों में से एक में लोडिंग त्रुटि) अन्य कोरूटीन को रद्द न करे। यह ViewModel परिदृश्य से मेल खाता है जहाँ विभिन्न स्क्रीन स्वतंत्र डेटा लोड करती हैं।
हाँ, viewModelScope किसी भी ViewModel में UI प्रकार (View System या Jetpack Compose) के बावजूद उपलब्ध है। Compose में, कोरूटीन भी viewModelScope के माध्यम से लॉन्च किए जाते हैं, जबकि UI प्रभावों के लिए LaunchedEffect और rememberCoroutineScope का उपयोग किया जाता है।
viewModelScope.cancel() कॉल करने से scope तुरंत रद्द हो जाता है — सभी सक्रिय कोरूटीन CancellationException के साथ समाप्त हो जाते हैं। यदि उसके बाद viewModelScope.launch कॉल किया जाता है, तो getter के अगले एक्सेस पर स्वचालित रूप से एक नया scope बनाया जाता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें