Τα Coroutines (κορουτίνες) είναι ελαφριά νήματα Kotlin για ασύγχρονο προγραμματισμό, διαθέσιμα μέσω της βιβλιοθήκης kotlinx.coroutines. Σύμφωνα με το JetBrains Kotlin Documentation, 2026, τα Coroutines επιτρέπουν την αναστολή εκτέλεσης μιας συνάρτησης χωρίς αποκλεισμό του νήματος, σε αντίθεση με τα παραδοσιακά Thread. Τα κορουτίνες εκτελούνται σε μια περιορισμένη ομάδα νημάτων, καθιστώντας τα χιλιάδες φορές ελαφρύτερα από τα εγγενή νήματα. Τα Kotlin Coroutines είναι πλήρως ενσωματωμένα με τα Android Jetpack, Retrofit, Room και άλλες δημοφιλείς βιβλιοθήκες του οικοσυστήματος Android.
Κύρια σημεία
Coroutines (κορουτίνες) είναι ένας μηχανισμός ασύγχρονου προγραμματισμού στο Kotlin, υλοποιημένος στη βιβλιοθήκη kotlinx.coroutines. Σε αντίθεση με τα νήματα του λειτουργικού συστήματος, τα coroutines δεν είναι δεσμευμένα σε ένα συγκεκριμένο νήμα: μπορούν να ανασταλούν σε ένα νήμα και να συνεχιστούν σε άλλο. Ένα νήμα μπορεί να εκτελέσει χιλιάδες coroutines, εναλλάσσοντας μεταξύ τους με ελάχιστο επιπλέον κόστος.
Τα coroutines εμφανίστηκαν στο Kotlin 1.3 (2018) ως πειραματική λειτουργία και έγιναν σταθερά στο Kotlin 1.5 (2021). Τα Coroutines λύνουν το πρόβλημα του callback hell παρόμοια με το async/await, αλλά προσφέρουν πλουσιότερο API: κανάλια (Channel), Flow, διαχείριση εξαιρέσεων στην ιεραρχία Job και άμεση ενσωμάτωση με το Android Lifecycle.
Σύμφωνα με τη JetBrains (2025), κάθε coroutine καταναλώνει περίπου 100 byte μνήμης έναντι 1+ MB για ένα εγγενές νήμα. Αυτό επιτρέπει την εκτέλεση εκατομμυρίων coroutines σε μία εφαρμογή χωρίς κίνδυνο OutOfMemoryError. Ακριβώς η ελαφρότητα των coroutines τα καθιστά το προτιμώμενο εργαλείο για ασυγχρονισμό στο Android.
Κάθε coroutine Kotlin μεταγλωττίζεται σε μια μηχανή καταστάσεων μέσω Continuation Passing Style (CPS). Ο μεταγλωττιστής προσθέτει μια κρυφή παράμετρο Continuation σε κάθε συνάρτηση suspend. Το Continuation περιέχει το σημείο συνέχισης και όλες τις τοπικές μεταβλητές. Όταν το coroutine αναστέλλεται, το runtime αποθηκεύει το Continuation, και όταν συνεχίζεται, το επαναφέρει σε οποιοδήποτε διαθέσιμο νήμα από την ομάδα του Dispatcher.
suspend — η λέξη-κλειδί του Kotlin που χαρακτηρίζει μια συνάρτηση ως αναστέλλιμη. Μια τέτοια συνάρτηση μπορεί να κληθεί μόνο από άλλη συνάρτηση suspend ή από coroutine. Μέσα σε μια συνάρτηση suspend μπορούν να κληθούν άλλες συναρτήσεις suspend με οποιαδήποτε σειρά, και κάθε σημείο κλήσης είναι ένα δυνητικό σημείο αναστολής.
Η μηχανική είναι απλή: όταν μια συνάρτηση suspend καλεί μια άλλη συνάρτηση suspend, αναστέλλεται σε εκείνο το σημείο, ελευθερώνοντας το νήμα. Μετά την ολοκλήρωση της καλούμενης συνάρτησης, το runtime συνεχίζει την εκτέλεση από την αποθηκευμένη θέση. Αυτό ονομάζεται cooperative cancellation — κανένα νήμα δεν μπλοκάρεται.
Σημαντικό: η συνάρτηση suspend δεν είναι ασύγχρονη από προεπιλογή. Η σειρά εκτέλεσης παραμένει σειριακή εάν δεν χρησιμοποιούνται launch ή async. Το suspend επιτρέπει απλώς στη συνάρτηση να ανασταλεί χωρίς να μπλοκάρει το νήμα και να αποτελεί μέρος του πλαισίου coroutine. Το Continuation Passing Style είναι ένα μοντέλο μεταγλώττισης στο οποίο κάθε συνάρτηση suspend λαμβάνει μια κρυφή callback Continuation και ο μεταγλωττιστής δημιουργεί μια μηχανή καταστάσεων για τη διαχείριση αναστολών και συνεχίσεων.
CoroutineScope είναι ένα πλαίσιο που καθορίζει τον κύκλο ζωής των coroutines. Όλα τα coroutines πρέπει να εκκινούνται μέσα σε ένα scope. Όταν το scope ακυρώνεται (π.χ. κατά την ολοκλήρωση μιας Activity), όλα τα θυγατρικά coroutines ακυρώνονται αυτόματα. Αυτό αποτρέπει διαρροές εργασιών παρασκηνίου. Το Android Jetpack παρέχει έτοιμα scopes για κάθε component: viewModelScope για ViewModel και lifecycleScope για Activity και Fragment, τα οποία ακυρώνονται αυτόματα κατά την καταστροφή του αντίστοιχου component.
Structured Concurrency — η αρχή που εγγυάται ότι ένα coroutine δεν θα ολοκληρωθεί έως ότου ολοκληρωθούν όλα τα θυγατρικά του coroutines. Η ιεραρχία Job σχηματίζει ένα δέντρο: το coroutine ρίζας δημιουργεί ένα parent job, τα παιδιά δημιουργούν child jobs. Η ακύρωση του parent job διαδίδεται σε όλα τα παιδιά. Το Structured Concurrency είναι η θεμελιώδης διαφορά μεταξύ coroutines και νημάτων.
| Scope | Πού χρησιμοποιείται | Ακύρωση |
|---|---|---|
| GlobalScope | Μόνο για εργασίες daemon | Δεν ακυρώνεται αυτόματα |
| viewModelScope | Android ViewModel | Κατά τον καθαρισμό ViewModel |
| lifecycleScope | Android Activity/Fragment | Κατά την καταστροφή lifecycle |
| coroutineScope | Μέσα σε συνάρτηση suspend | Κατά την ακύρωση του parent job |
Το συνηθισμένο Job σε σφάλμα ενός θυγατρικού coroutine ακυρώνει όλα τα siblings. Το SupervisorJob αποτελεί εξαίρεση: μια αποτυχία σε ένα θυγατρικό coroutine δεν επηρεάζει τα υπόλοιπα. Αυτό είναι σημαντικό όταν πολλές ανεξάρτητες εργασίες εκτελούνται παράλληλα και μία μπορεί να αποτύχει χωρίς να χρειαστεί να ακυρωθούν οι άλλες.
Dispatchers καθορίζουν σε ποια νήματα εκτελούνται τα coroutines. Dispatchers.Main — το κύριο νήμα UI του Android. Dispatchers.IO — ομάδα για λειτουργίες αποκλεισμού (δίκτυο, δίσκος). Dispatchers.Default — για εργασίες έντασης CPU. Dispatchers.Unconfined — ξεκινά στο τρέχον νήμα αλλά δεν εγγυάται παραμονή σε αυτό. Η επιλογή του σωστού Dispatcher είναι κρίσιμη για την απόδοση: μια εργασία IO στο Default θα μπλοκάρει την ομάδα για υπολογισμούς, ενώ μια εργασία CPU στο IO θα δημιουργήσει περιττά νήματα.
withContext — συνάρτηση για εναλλαγή Dispatcher μέσα στο coroutine. Για παράδειγμα, μια συνάρτηση suspend που αναλύει JSON μπορεί να αλλάξει σε Dispatchers.Default κατά τον υπολογισμό και να επιστρέψει σε Dispatchers.Main για ενημέρωση UI. Το withContext είναι ο πιο χρησιμοποιούμενος δημιουργός στην ανάπτυξη Android.
launch — εκκινεί ένα coroutine, επιστρέφει Job, δεν επιστρέφει αποτέλεσμα (fire-and-forget). async — εκκινεί ένα coroutine, επιστρέφει Deferred από το οποίο μπορεί να ληφθεί το αποτέλεσμα μέσω await. runBlocking — μπλοκάρει το τρέχον νήμα για την εκτέλεση του coroutine (μόνο για δοκιμές και main συναρτήσεις). Επιλογή δημιουργού εξαρτάται από το σενάριο: το launch είναι κατάλληλο για γεγονότα και ενημερώσεις, το async για εργασίες με αποτέλεσμα, το runBlocking μόνο για δοκιμές ή σημεία εισόδου.
Ας εξετάσουμε τρία πρακτικά σενάρια: βασικό coroutine με launch, παράλληλη κλήση με async και διαχείριση σφαλμάτων με SupervisorJob.
viewModelScope.launch εκκινεί ένα coroutine στο πλαίσιο του ViewModel. Κατά τον καθαρισμό του ViewModel, το coroutine ακυρώνεται αυτόματα.
class ProfileViewModel : ViewModel() {
fun loadUser() {
viewModelScope.launch(Dispatchers.IO) {
val user = api.fetchUser()
withContext(Dispatchers.Main) {
showUser(user)
}
}
}
}
coroutineScope με async εκκινεί τρία αιτήματα παράλληλα. Τα αποτελέσματα συλλέγονται μέσω .await(). Εάν οποιοδήποτε αίτημα αποτύχει, όλα ακυρώνονται.
suspend fun loadDashboard(): Dashboard = coroutineScope {
val user = async { api.fetchUser() }
val posts = async { api.fetchPosts() }
val stats = async { api.fetchStats() }
Dashboard(user.await(), posts.await(), stats.await())
}
SupervisorJob επιτρέπει σε κάθε coroutine να ολοκληρωθεί ανεξάρτητα. Ένα σφάλμα σε ένα αίτημα δεν ακυρώνει τα υπόλοιπα.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
try { api.fetchPosts() } catch (e: Exception) { log(e) }
}
Νήματα (Thread) — πρωτόγονο του λειτουργικού συστήματος. Κάθε νήμα έχει τη δική του στοίβα (~1 MB) και απαιτεί κλήση συστήματος για δημιουργία και εναλλαγή. Coroutines — πρωτόγονο γλώσσας, μη δεσμευμένο στο OS. Χρησιμοποιούν Continuation για αποθήκευση κατάστασης και εναλλάσσονται σε επίπεδο runtime χωρίς κλήσεις συστήματος.
Σύμφωνα με την Google (2025), η χρήση coroutines αντί για νήματα μειώνει την κατανάλωση μνήμης για εργασίες παρασκηνίου σε εφαρμογές Android κατά 90–95%. Όλες οι σύγχρονες βιβλιοθήκες Android (Retrofit, Room, WorkManager) έχουν ενσωματωμένη υποστήριξη για coroutines μέσω συναρτήσεων suspend. Το Ktor (το πλαίσιο HTTP πελάτη της JetBrains) είναι επίσης πλήρως χτισμένο σε coroutines, παρέχοντας συναρτήσεις-suspend για κάθε αίτημα χωρίς callback API. Το Room υποστηρίζει coroutines μέσω συναρτήσεων-suspend στο DAO, επιτρέποντας την εκτέλεση ερωτημάτων βάσης δεδομένων χωρίς αποκλεισμό του κύριου νήματος.
Τα νήματα παραμένουν απαραίτητα για εγγενή κώδικα μέσω JNI, κλήσεις αποκλεισμού έντασης CPU χωρίς χρονικό περιορισμό (απόδοση βίντεο, προσομοιώσεις) και κατά την ενσωμάτωση με βιβλιοθήκες C. Για οτιδήποτε άλλο — coroutines.
Συχνές Ερωτήσεις
Coroutine — μια αναστέλλιμη μονάδα εργασίας που εκτελείται σε ένα υπάρχον νήμα. Το νήμα είναι ένας πόρος συστήματος με δική του στοίβα. Τα coroutines είναι χιλιάδες φορές ελαφρύτερα από τα νήματα και δεν μπλοκάρουν πόρους κατά την αναστολή.
Dispatchers.IO προορίζεται για λειτουργίες I/O αποκλεισμού (δίκτυο, αρχεία) και μπορεί να δημιουργεί νέα νήματα όταν χρειάζεται. Dispatchers.Default έχει σταθερή ομάδα (αριθμός πυρήνων CPU) για υπολογισμούς έντασης CPU.
Job.cancel() ακυρώνει το coroutine και όλα τα παιδιά του. Για έλεγχο ακύρωσης μέσα στο coroutine, χρησιμοποιήστε ensureActive() — πετάει CancellationException εάν το coroutine έχει ακυρωθεί.
Ναι — μέσω της βιβλιοθήκης kotlinx-coroutines-rx3. Παρέχει συναρτήσεις awaitSingle, awaitFirst και άλλες για μετατροπή Observable/Single σε συναρτήσεις suspend και αντίστροφα μέσω flowable.
Flow — μια ψυχρή ασύγχρονη ροή δεδομένων, το αντίστοιχο του RxJava Observable για coroutines. Το Flow εκπέμπει τιμές σειριακά και τελειώνει με εξαίρεση ή επιτυχία. Υποστηρίζει τους τελεστές map, filter, catch και άλλους.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης