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 падне испод наведеног стања и настављали при повратку. Међутим, означени су као @Deprecated у lifecycle-runtime-ktx 2.6.0, јер:
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)
}
}
}
Подаци се синхронизују сваких 30 секунди, али само када је екран видљив. isActive проверава да ли корутина није отказана, што омогућава безбедан излазак из петље при напуштању екрана.
Оба scope-а су везана за животни циклус, али за његове различите аспекте. Разумевање разлике је критично за исправну архитектуру Android апликација.
viewModelScope је везан за ViewModel, који преживљава ротацију екрана. lifecycleScope је везан за LifecycleOwner (Activity/Fragment), који се при ротацији уништава и поново креира. То одређује сценарије њихове примене.
У пракси се често среће комбинација оба scope-а: viewModelScope учитава податке и управља стањем, lifecycleScope се претплаћује на Flow из ViewModel-а са уважавањем животног циклуса екрана. Ово раздвајање одговорности сматра се best practice у модерном Android развоју.
Размотримо четири најчешће грешке које програмери праве при коришћењу 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-у може, што доводи до падова. Увек користите lifecycleScope у UI слоју.
Не, ViewModel није LifecycleOwner, па lifecycleScope није доступан у њему. ViewModel користи viewModelScope. Ако код треба да се извршава у оба контекста — издвојите логику у use case или repository са suspend функцијама.
Сваки позив 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 креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође