lifecycleScope — είναι ένα ενσωματωμένο CoroutineScope από τη βιβλιοθήκη androidx.lifecycle, το οποίο είναι συνδεδεμένο με τον κύκλο ζωής ενός Activity, Fragment ή οποιουδήποτε LifecycleOwner και ακυρώνει αυτόματα τα coroutine όταν καταστρέφεται το component. Σύμφωνα με το Google Android Developers, 2025, το lifecycleScope επιτρέπει την ασφαλή εκτέλεση coroutine που σχετίζονται με το επίπεδο UI χωρίς τον κίνδυνο εκτέλεσης κώδικα μετά την καταστροφή του Activity ή του Fragment. Το scope ακυρώνεται αυτόματα όταν το LifecycleOwner μεταβαίνει στην κατάσταση DESTROYED.
Κύρια σημεία
lifecycleScope — είναι μια ιδιότητα επέκτασης στη διεπαφή LifecycleOwner (Activity, Fragment, Service) που παρέχει ένα έτοιμο CoroutineScope συνδεδεμένο με τον πλήρη κύκλο ζωής του component. Όταν το LifecycleOwner φτάσει στην κατάσταση DESTROYED, το lifecycleScope ακυρώνει αυτόματα όλα τα ενεργά coroutine.
// Σε 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 παρέχει διάφορους τρόπους εκκίνησης coroutine μέσω του lifecycleScope. Ας δούμε την εξέλιξη του API από παρωχημένες μεθόδους σε σύγχρονες.
Ο απλούστερος τρόπος — lifecycleScope.launch { ... }. Το coroutine ξεκινά αμέσως και ακυρώνεται στο DESTROYED. Ωστόσο, μπορεί να εκτελεί κώδικα ακόμα και όταν το UI δεν είναι ορατό (π.χ. στο παρασκήνιο μετά το onStop). Αυτό δεν είναι πάντα επιθυμητό.
Αυτές οι μέθοδοι ανέστελλαν την εκτέλεση του coroutine όταν το Lifecycle έπεφτε κάτω από την καθορισμένη κατάσταση και τη συνέχιζαν κατά την επιστροφή. Ωστόσο, επισημάνθηκαν ως @Deprecated στο lifecycle-runtime-ktx 2.6.0, επειδή:
repeatOnLifecycle — ο προτεινόμενος από την Google τρόπος εκκίνησης coroutine συγχρονισμένων με τον κύκλο ζωής. Ακυρώνει και επανεκκινεί το coroutine κάθε φορά που το Lifecycle φτάνει σε μια καθορισμένη κατάσταση.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
Το coroutine που μεταβιβάζεται στο repeatOnLifecycle ξεκινά όταν το Lifecycle φτάσει στο STARTED και ακυρώνεται όταν πέσει κάτω από το STARTED. Κατά την επιστροφή στο STARTED, το coroutine επανεκκινείται από την αρχή. Αυτό είναι ασφαλές και αποτελεσματικό — κανένα coroutine δεν παραμένει σε αναστολή.
Για τη συλλογή δεδομένων από 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)
}
}
}
}
}
Το coroutine ξεκινά όταν το fragment γίνεται ορατό (STARTED) και ακυρώνεται όταν φεύγει από την οθόνη (STOPPED). Αν ο χρήστης μεταβεί σε άλλη εφαρμογή, οι ενημερώσεις τοποθεσίας δεν καταναλώνουν μπαταρία.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
Το κινούμενο σχέδιο ξεκινά μόνο όταν το fragment είναι σε πρώτο πλάνο (RESUMED). Αν ο χρήστης ελαχιστοποιήσει την εφαρμογή κατά τη διάρκεια του κινούμενου σχεδίου, το coroutine ακυρώνεται και κατά την επιστροφή το κινούμενο σχέδιο ξεκινά ξανά.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
Τα δεδομένα συγχρονίζονται κάθε 30 δευτερόλεπτα, αλλά μόνο όταν η οθόνη είναι ορατή. Το isActive ελέγχει αν το coroutine δεν έχει ακυρωθεί, διασφαλίζοντας ασφαλή έξοδο από τον βρόχο κατά την αποχώρηση από την οθόνη.
Και τα δύο scope είναι συνδεδεμένα με τον κύκλο ζωής, αλλά με διαφορετικές πτυχές του. Η κατανόηση της διαφοράς είναι κρίσιμη για τη σωστή αρχιτεκτονική εφαρμογών Android.
viewModelScope είναι συνδεδεμένο με το ViewModel, το οποίο επιβιώνει της περιστροφής οθόνης. lifecycleScope είναι συνδεδεμένο με το LifecycleOwner (Activity/Fragment), το οποίο κατά την περιστροφή καταστρέφεται και δημιουργείται εκ νέου. Αυτό καθορίζει τα σενάρια εφαρμογής τους.
Στην πράξη, ο συνδυασμός και των δύο scope είναι συχνός: το viewModelScope φορτώνει δεδομένα και διαχειρίζεται την κατάσταση, το lifecycleScope εγγράφεται σε Flow από το ViewModel λαμβάνοντας υπόψη τον κύκλο ζωής της οθόνης. Αυτός ο διαχωρισμός ευθυνών θεωρείται best practice στη σύγχρονη ανάπτυξη Android.
Ας δούμε τέσσερα συνηθισμένα λάθη που κάνουν οι προγραμματιστές κατά τη χρήση του lifecycleScope.
Εάν ξεκινήσετε τη φόρτωση δεδομένων σε lifecycleScope.launch, κατά την περιστροφή οθόνης το coroutine θα ακυρωθεί και τα δεδομένα θα πρέπει να φορτωθούν ξανά. Χρησιμοποιήστε viewModelScope για μακροχρόνιες λειτουργίες. lifecycleScope — μόνο για εργασίες που σχετίζονται με UI.
Η άμεση κλήση viewModel.someFlow.collect { ... } μέσα σε lifecycleScope.launch συνεχίζει να συλλέγει δεδομένα ακόμα και όταν η οθόνη δεν είναι ορατή. Αυτό μπορεί να οδηγήσει σε ενημερώσεις UI στο παρασκήνιο και περιττή επιβάρυνση. Πάντα να χρησιμοποιείτε repeatOnLifecycle ή flowWithLifecycle.
Παρόλο που το lifecycleScope ακυρώνεται στο DESTROYED, ο κώδικας μετά από ένα σημείο αναστολής (suspend) μπορεί να μην εκτελεστεί σε περίπτωση ξαφνικής ακύρωσης. Μην βασίζεστε στην εκτέλεση κώδικα μετά από κλήση suspend, εκτός εάν χρησιμοποιείτε NonCancellable.
Το launchWhenStarted και τα ανάλογά του δεν ακυρώνουν το coroutine, μόνο το αναστέλλουν. Εάν η οθόνη εναλλάσσεται πολλές φορές μεταξύ πρώτου και δεύτερου πλάνου, το coroutine συσσωρεύει αναβληθείσες κλήσεις. Μεταβείτε στο repeatOnLifecycle — αυτός είναι ο μόνος σωστός τρόπος συγχρονισμού με το Lifecycle.
Συχνές Ερωτήσεις
lifecycleScope ακυρώνεται αυτόματα όταν καταστρέφεται το LifecycleOwner. GlobalScope ζει καθ' όλη τη διάρκεια λειτουργίας της εφαρμογής. Το coroutine στο lifecycleScope δεν μπορεί να ενημερώσει το UI μετά την καταστροφή του component, στο GlobalScope μπορεί, οδηγώντας σε σφάλματα. Πάντα να χρησιμοποιείτε lifecycleScope στο επίπεδο UI.
Όχι, το ViewModel δεν είναι LifecycleOwner, επομένως το lifecycleScope δεν είναι διαθέσιμο σε αυτό. Το ViewModel χρησιμοποιεί viewModelScope. Εάν ο κώδικας πρέπει να εκτελείται και στα δύο πλαίσια — εξάγετε τη λογική σε use case ή repository με συναρτήσεις suspend.
Κάθε κλήση repeatOnLifecycle δημιουργεί ένα νέο coroutine που εκτελεί το μπλοκ όταν το Lifecycle φτάσει στην καθορισμένη κατάσταση. Εάν το repeatOnLifecycle κληθεί δύο φορές για την ίδια κατάσταση, και τα δύο μπλοκ θα εκτελεστούν ανεξάρτητα. Συνήθως αρκεί μία κλήση στο onViewCreated.
Ο dispatcher του lifecycleScope δεν μπορεί να αλλάξει άμεσα — χρησιμοποιεί Dispatchers.Main.immediate. Μέσα στο μπλοκ coroutine, μπορείτε να μεταβείτε σε άλλο dispatcher μέσω withContext. Για δοκιμές, χρησιμοποιήστε TestDispatcher με LifecycleOwner.
Το lifecycleScope ακυρώνεται όταν το LifecycleOwner μεταβαίνει στην κατάσταση DESTROYED (μετά το onDestroy). Οι απλές κλήσεις lifecycleScope.launch δεν ακυρώνονται στο onPause ή στο onStop. Για παύση κατά τη μετάβαση στο παρασκήνιο, χρησιμοποιήστε repeatOnLifecycle(STARTED) ή repeatOnLifecycle(RESUMED).
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης