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) ڈیباؤنس کو نافذ کرتا ہے — تلاش صرف 300 ms کی غیرفعالیت کے بعد انجام دی جاتی ہے۔ یہ سرور کے بوجھ کو کم کرتا ہے اور پرانے نتائج کو روکتا ہے۔
دونوں 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 استعمال کریں یا کیچ بلاک کی تکمیل پر بھروسہ کریں۔
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 UI کی قسم (View System یا Jetpack Compose) سے قطع نظر کسی بھی ViewModel میں دستیاب ہے۔ Compose میں، کوروٹین بھی viewModelScope کے ذریعے شروع کی جاتی ہیں، جبکہ UI اثرات کے لیے LaunchedEffect اور rememberCoroutineScope استعمال ہوتے ہیں۔
viewModelScope.cancel() کال کرنے سے scope فوری طور پر منسوخ ہو جاتا ہے — تمام فعال کوروٹین CancellationException کے ساتھ ختم ہو جاتی ہیں۔ اگر اس کے بعد viewModelScope.launch کال کیا جائے تو، getter تک اگلی رسائی پر خود بخود ایک نیا scope بن جاتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں