lifecycleScope — یک CoroutineScope داخلی از کتابخانهٔ androidx.lifecycle است که به چرخهٔ حیات Activity، Fragment یا هر LifecycleOwner متصل میشود و هنگام نابودی کامپوننت بهطور خودکار کوروتینها را لغو میکند. به گفتهٔ Google Android Developers, 2025، lifecycleScope امکان اجرای ایمن کوروتینهای متصل به لایهٔ UI را بدون خطر اجرای کد پس از نابودی Activity یا Fragment فراهم میکند. scope با ورود LifecycleOwner به حالت DESTROYED بهطور خودکار لغو میشود.
نکات اصلی
lifecycleScope — یک ویژگی توسعهای بر روی رابط LifecycleOwner (Activity، Fragment، Service) است که یک CoroutineScope آماده و متصل به چرخهٔ کامل حیات کامپوننت ارائه میدهد. هنگامی که LifecycleOwner به حالت DESTROYED میرسد، lifecycleScope بهطور خودکار همهٔ کوروتینهای فعال را لغو میکند.
// در Fragment یا Activity
lifecycleScope.launch {
delay(1000)
showSnackbar("سلام!")
}
برخلاف viewModelScope، lifecycleScope در هر بار نابودی LifecycleOwner — از جمله چرخش صفحه — لغو میشود. این آن را برای عملیاتی که فقط باید تا زمانی که صفحهٔ خاصی قابل مشاهده است زنده بمانند ایدهآل میکند.
lifecycleScope در هر جایی که LifecycleOwner وجود دارد در دسترس است:
مکانیسم لغو خودکار lifecycleScope بر اساس اشتراک رویدادهای Lifecycle است. هنگامی که Lifecycle از CREATED به DESTROYED پایین میآید، scope لغو میشود.
| وضعیت | توضیح | Scope فعال |
|---|---|---|
| CREATED | LifecycleOwner ایجاد شده، onCreate اجرا شده | بله |
| STARTED | LifecycleOwner قابل مشاهده (onStart) | بله |
| RESUMED | LifecycleOwner در پیشزمینه (onResume) | بله |
| DESTROYED | LifecycleOwner نابود شده (onDestroy) | خیر (scope لغو شده) |
lifecycleScope به عنوان CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) ایجاد میشود و در داخل Lifecycle ذخیره میگردد. هنگام انتقال Lifecycle به حالت DESTROYED، scope.cancel() فراخوانی میشود. این مکانیسم از طریق LifecycleEventObserver پیادهسازی میشود که در اولین دسترسی به scope در رویدادهای چرخهٔ حیات مشترک میشود.
هنگام چرخش صفحه، Activity نابود میشود (onDestroy) و دوباره ایجاد میگردد. lifecycleScope همراه با Activity قدیمی لغو میشود و یک نمونهٔ جدید از scope برای Activity جدید ایجاد میگردد. این تفاوت اساسی با viewModelScope است که در هنگام چرخش حفظ میشود.
کتابخانهٔ lifecycle چندین روش برای راهاندازی کوروتینها از طریق lifecycleScope ارائه میدهد. بیایید تکامل API را از روشهای منسوخ به مدرن بررسی کنیم.
سادهترین روش — lifecycleScope.launch { ... }. کوروتین بلافاصله شروع میشود و در DESTROYED لغو میگردد. با این حال ممکن است حتی زمانی که UI قابل مشاهده نیست (مثلاً در پسزمینه پس از onStop) کد اجرا کند. این همیشه مطلوب نیست.
این روشها وقتی Lifecycle پایینتر از وضعیت مشخص شده میرفت اجرای کوروتین را متوقف میکردند و هنگام بازگشت از سر میگرفتند. اما آنها در lifecycle-runtime-ktx 2.6.0 به عنوان @Deprecated علامتگذاری شدند، زیرا:
repeatOnLifecycle — روش توصیه شده توسط Google برای راهاندازی کوروتینهای همگامسازی شده با چرخهٔ حیات است. هر بار که Lifecycle به وضعیت مشخصی میرسد، کوروتین را لغو و دوباره راهاندازی میکند.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
کوروتینی که به repeatOnLifecycle منتقل میشود وقتی Lifecycle به STARTED میرسد شروع میشود و وقتی به زیر STARTED میرود لغو میگردد. هنگام بازگشت به STARTED کوروتین دوباره از نو شروع میشود. این ایمن و کارآمد است — هیچ کوروتینی در حالت تعلیق باقی نمیماند.
برای جمعآوری داده از Flow با در نظر گرفتن چرخهٔ حیات، عملگر flowWithLifecycle وجود دارد. این عملگر بهطور خودکار جمعآوری را هنگام تغییر وضعیت Lifecycle متوقف و از سر میگیرد:
viewModel.uiState
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { state -> updateUI(state) }
.launchIn(lifecycleScope)
عملگر flowWithLifecycle — مختصرترین راه برای اشتراک ایمن Flow در لایهٔ UI است.
سه سناریوی واقعی استفاده از lifecycleScope در برنامهٔ Android با Kotlin را بررسی میکنیم.
class MapFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.observeLocation().collect { loc ->
updateMapMarker(loc)
}
}
}
}
}
کوروتین وقتی فرگمنت قابل مشاهده میشود (STARTED) شروع میشود و وقتی از صفحه خارج میشود (STOPPED) لغو میگردد. اگر کاربر به برنامهٔ دیگری سوئیچ کند، بهروزرسانیهای موقعیت مکانی باتری مصرف نمیکنند.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
انیمیشن فقط زمانی شروع میشود که فرگمنت در پیشزمینه است (RESUMED). اگر کاربر در حین انیمیشن برنامه را ببندد، کوروتین لغو میشود و هنگام بازگشت انیمیشن دوباره شروع میگردد.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
دادهها هر ۳۰ ثانیه همگامسازی میشوند، اما فقط زمانی که صفحه قابل مشاهده است. isActive بررسی میکند که آیا کوروتین لغو نشده است، که خروج ایمن از حلقه را هنگام ترک صفحه تضمین میکند.
هر دو scope به چرخهٔ حیات متصل هستند، اما به جنبههای مختلف آن. درک تفاوت برای معماری صحیح برنامههای Android حیاتی است.
viewModelScope به ViewModel متصل است که از چرخش صفحه جان سالم به در میبرد. lifecycleScope به LifecycleOwner (Activity/Fragment) متصل است که هنگام چرخش نابود و دوباره ایجاد میشود. این سناریوهای کاربرد آنها را تعیین میکند.
در عمل، ترکیب هر دو scope رایج است: viewModelScope داده را بارگذاری کرده و وضعیت را مدیریت میکند، lifecycleScope با در نظر گرفتن چرخهٔ حیات صفحه در Flow از ViewModel مشترک میشود. این تفکیک مسئولیت در توسعهٔ مدرن Android یک best practice محسوب میشود.
چهار خطای رایجی که توسعهدهندگان هنگام استفاده از lifecycleScope مرتکب میشوند را بررسی میکنیم.
اگر بارگذاری داده را در lifecycleScope.launch شروع کنید، هنگام چرخش صفحه کوروتین لغو میشود و داده باید دوباره بارگذاری شود. برای عملیات طولانیمدت از viewModelScope استفاده کنید. lifecycleScope — فقط برای وظایف مرتبط با UI.
فراخوانی مستقیم viewModel.someFlow.collect { ... } درون lifecycleScope.launch حتی زمانی که صفحه قابل مشاهده نیست به جمعآوری داده ادامه میدهد. این میتواند منجر به بهروزرسانی UI در پسزمینه و سربار اضافی شود. همیشه از repeatOnLifecycle یا flowWithLifecycle استفاده کنید.
اگرچه lifecycleScope در DESTROYED لغو میشود، کد پس از نقطهٔ تعلیق (suspend) ممکن است در لغو ناگهانی اجرا نشود. به اجرای کد پس از فراخوانی suspend اعتماد نکنید، مگر اینکه از NonCancellable استفاده کنید.
launchWhenStarted و مشابههای آن کوروتین را لغو نمیکنند، فقط متوقف میکنند. اگر صفحه چندین بار بین پیشزمینه و پسزمینه جابجا شود، کوروتین فراخوانیهای معوق را انباشته میکند. به repeatOnLifecycle مهاجرت کنید — این تنها راه صحیح همگامسازی با Lifecycle است.
سؤالات متداول
lifecycleScope هنگام نابودی LifecycleOwner بهطور خودکار لغو میشود. GlobalScope در تمام طول عمر برنامه زنده میماند. کوروتین در lifecycleScope نمیتواند پس از نابودی کامپوننت UI را بهروزرسانی کند، در GlobalScope میتواند که منجر به خطا میشود. همیشه در لایهٔ UI از lifecycleScope استفاده کنید.
خیر، ViewModel یک LifecycleOwner نیست، بنابراین lifecycleScope در آن در دسترس نیست. ViewModel از viewModelScope استفاده میکند. اگر کد باید در هر دو زمینه اجرا شود — منطق را با توابع suspend به use case یا repository منتقل کنید.
هر فراخوانی repeatOnLifecycle یک کوروتین جدید ایجاد میکند که بلاک را هنگام رسیدن به وضعیت مشخص Lifecycle راهاندازی میکند. اگر repeatOnLifecycle دو بار برای یک وضعیت فراخوانی شود، هر دو بلاک مستقل اجرا میشوند. معمولاً یک بار فراخوانی در onViewCreated کافی است.
نمیتوان مستقیماً توزیعکنندهٔ lifecycleScope را تغییر داد — از Dispatchers.Main.immediate استفاده میکند. درون بلاک کوروتین میتوانید از طریق withContext به توزیعکنندهٔ دیگری سوئیچ کنید. برای تستها از TestDispatcher با LifecycleOwner استفاده کنید.
lifecycleScope هنگام ورود LifecycleOwner به حالت DESTROYED (پس از onDestroy) لغو میشود. فراخوانیهای سادهٔ lifecycleScope.launch در onPause یا onStop لغو نمیشوند. برای توقف هنگام رفتن به پسزمینه از repeatOnLifecycle(STARTED) یا repeatOnLifecycle(RESUMED) استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید