EventBus: τι είναι, αρχή λειτουργίας και λεωφορείο συμβάντων Android

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-18 Χρόνος ανάγνωσης: 10 λεπ

EventBus είναι μια βιβλιοθήκη για Android που υλοποιεί το μοτίβο Publisher-Subscriber μέσω λεωφορείου συμβάντων, επιτρέποντας την ανταλλαγή δεδομένων μεταξύ στοιχείων χωρίς άμεση εξάρτηση. Αναπτυγμένη από την GreenRobot, η βιβλιοθήκη απλοποιεί την επικοινωνία μεταξύ Activity, Fragment, Service και Background Thread. Σύμφωνα με τα δεδομένα του GitHub (2025), το EventBus έχει πάνω από 25 χιλιάδες αστέρια και χρησιμοποιείται σε χιλιάδες εφαρμογές Android. Οι κύριες λειτουργίες είναι subscribe (εγγραφή σε συμβάν), post (αποστολή συμβάντος) και sticky event (καθυστερημένο συμβάν για νέους συνδρομητές).

Κύρια σημεία

  • EventBus — βιβλιοθήκη λεωφορείου συμβάντων για χαλαρά συνδεδεμένη επικοινωνία στο Android.
  • @Subscribe — σχόλιο που σηματοδοτεί μια μέθοδο ως χειριστή συμβάντος συγκεκριμένου τύπου.
  • EventBus.getDefault().post() στέλνει το συμβάν σε όλους τους εγγεγραμμένους χειριστές.
  • Sticky event διατηρεί το τελευταίο συμβάν για παράδοση σε νέους συνδρομητές.
  • ThreadMode καθορίζει το νήμα εκτέλεσης του χειριστή: MAIN, POSTING, BACKGROUND, ASYNC.

Τι είναι το EventBus;

EventBus είναι μια βιβλιοθήκη λεωφορείου συμβάντων για Android, που υλοποιεί το μοτίβο Publisher-Subscriber (εκδότης-συνδρομητής). Επιτρέπει τη μεταφορά συμβάντων μεταξύ στοιχείων της εφαρμογής (Activity, Fragment, Service, ViewModel) χωρίς τη δημιουργία ρητών εξαρτήσεων μεταξύ τους. Σε αντίθεση με τους τυπικούς μηχανισμούς Android (Intent, BroadcastReceiver), το EventBus λειτουργεί εντός της διεργασίας και δεν χρησιμοποιεί IPC. Η βιβλιοθήκη είναι βελτιστοποιημένη για απόδοση και δεν χρησιμοποιει αντανάκλαση όταν το Subscriber Index είναι σωστά ρυθμισμένο.

GreenRobot EventBus: αρχιτεκτονική

Η αρχιτεκτονική του EventBus αποτελείται από τρία βασικά στοιχεία: Event (κλάση POJO με δεδομένα), Subscriber (αντικείμενο με μεθόδους σηματοδοτημένες με @Subscribe) και EventBus (κεντρικός διανομέας). Ο συνδρομητής εγγράφεται μέσω EventBus.getDefault().register(this), η διαγραφή — μέσω unregister(this). Τα συμβάντα είναι τυποποιημένα: οι χειριστές εγγράφονται σε μια συγκεκριμένη κλάση συμβάντος και καλούνται μόνο κατά την αποστολή συμβάντος αυτής της κλάσης ή των υποκλάσεών της.

kotlin
// Συμβάν POJO
data class MessageEvent(
    val message: String,
    val timestamp: Long = System.currentTimeMillis()
)

// Συνδρομητής στο Activity
class MainActivity : AppCompatActivity() {

    override fun onStart() {
        super.onStart()
        EventBus.getDefault().register(this)
    }

    override fun onStop() {
        EventBus.getDefault().unregister(this)
        super.onStop()
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onMessageEvent(event: MessageEvent) {
        textView.text = event.message
    }
}

// Αποστολή συμβάντος από άλλο στοιχείο
EventBus.getDefault().post(MessageEvent("Hello from Service"))

Subscriber Index για απόδοση

Από προεπιλογή, το EventBus χρησιμοποιεί αντανάκλαση για την εύρεση μεθόδων @Subscribe κατά την register(). Subscriber Index δημιουργεί ένα ευρετήριο χειριστών κατά τη φάση μεταγλώττισης μέσω ενός επεξεργαστή σχολιασμών. Αυτό εξαλείφει την επιβάρυνση της αντανάκλασης και επιταχύνει την εγγραφή. Για ενεργοποίηση, προσθέστε το eventbus-annotation-processor στο build.gradle. Το EventBus χρησιμοποιεί αυτόματα το ευρετήριο αν είναι διαθέσιμο στο classpath. Χωρίς ευρετήριο, η βιβλιοθήκη εξακολουθεί να λειτουργεί, αλλά με ελαφρά μείωση απόδοσης.

Πώς λειτουργεί το EventBus στο Android;

Κατά την κλήση EventBus.getDefault().post(event), η βιβλιοθήκη καθορίζει τον τύπο συμβάντος, βρίσκει όλους τους εγγεγραμμένους συνδρομητές με μεθόδους @Subscribe που αποδέχονται αυτόν τον τύπο και τους καλεί σύμφωνα με το καθορισμένο ThreadMode. Η αναζήτηση συνδρομητών πραγματοποιείται μέσω του χάρτη Class → CopyOnWriteArrayList, που δημιουργείται κατά την εγγραφή. Εάν το συμβάν δεν έχει συνδρομητές, το post ολοκληρώνεται χωρίς σφάλμα — αυτή είναι συμπεριφορά safe-fail.

Κύκλος ζωής εγγραφής

Ο συνδρομητής πρέπει να εγγραφεί στο onStart() και να διαγραφεί στο onStop(). Εάν εγγραφεί στο onCreate() και διαγραφεί στο onDestroy(), ένα Activity που καταστράφηκε χωρίς κλήση onDestroy (λόγω finish()) μπορεί να παραμείνει στη λίστα συνδρομητών. Διαρροή συνδρομητή — ένα από τα κύρια προβλήματα του EventBus: ένα Activity που παραμένει στη λίστα συνδρομητών δεν θα συλλεχθεί από τον GC έως ότου διαγραφεί. Πάντα να ζευγαρώνετε register/unregister στις σωστές μεθόδους κύκλου ζωής.

Προτεραιότητα χειριστών

Το σχόλιο @Subscribe υποστηρίζει την παράμετρο priority (ακέραιος αριθμός, προεπιλογή 0). Οι χειριστές με υψηλότερη προτεραιότητα καλούνται νωρίτερα. cancelEventDelivery() επιτρέπει τη διακοπή της παράδοσης συμβάντος στους υπόλοιπους συνδρομητές. Αυτό είναι χρήσιμο για χειριστές προτεραιότητας (καταγραφή, έλεγχος ταυτότητας) που μπορούν να ακυρώσουν την επεξεργασία συμβάντος από χαμηλότερους συνδρομητές. Η λειτουργία είναι διαθέσιμη μόνο στο νήμα αποστολής συμβάντος.

kotlin
// Σύνθετο παράδειγμα με προτεραιότητα
data class NavigationEvent(val screen: String, val data: Bundle)

class NavigationInterceptor {
    @Subscribe(priority = 10, threadMode = ThreadMode.POSTING)
    fun onNavigationEvent(event: NavigationEvent) {
        if (event.screen == "restricted" && !isAuthorized) {
            EventBus.getDefault().cancelEventDelivery(event)
        }
    }
}

class AnalyticsLogger {
    @Subscribe(priority = 5)
    fun logNavigation(event: NavigationEvent) {
        analytics.logScreen(event.screen)
    }
}

// Αποστολή συμβάντος
EventBus.getDefault().post(NavigationEvent("profile", bundle))

EventBus vs LocalBroadcastManager vs LiveData

Το Android προσφέρει διάφορους μηχανισμούς για ενδοδιεργασιακή επικοινωνία: EventBus, LocalBroadcastManager (παροχημένο) και LiveData/Flow. Κάθε ένα έχει τα πλεονεκτήματα και μειονεκτήματά του. Η επιλογή εξαρτάται από την αρχιτεκτονική προσέγγιση και τις απαιτήσεις απόδοσης. Οι σύγχρονες συστάσεις της Google κλίνουν προς LiveData και Flow λόγω ενσωμάτωσης με το Lifecycle και απουσίας διαρροών.

ΧαρακτηριστικόEventBusLocalBroadcastManagerLiveData / Flow
ΤυποποίησηΜέσω κλάσης συμβάντοςΜέσω Intent filter (String)Μέσω γενικού τύπου
Lifecycle-awareΌχι (χειροκίνητη διαγραφή)Όχι (χειροκίνητη διαγραφή)Ναι (αυτόματα)
StickyΝαι (postSticky)ΌχιΝαι (LiveData — πάντα sticky)
ThreadModeMAIN, POSTING, BACKGROUND, ASYNCΜόνο mainΜέσω observe/observeOn
ΑπόδοσηΥψηλή (Subscriber Index)Μεσαία (περιτύλιγμα IPC)Υψηλή (παρατήρηση)

Πότε το EventBus είναι προτιμότερο

Το EventBus είναι χρήσιμο σε έργα με κληρονομικό κώδικα (legacy code) και όπου τα LiveData/Flow δεν είναι διαθέσιμα (έργα Java-only). Τα Sticky events του EventBus παρέχουν ευελιξία που λείπει από το LocalBroadcastManager. Το EventBus είναι επίσης απλούστερο για αποστολή συμβάντων από Service σε Activity χωρίς ViewModel — ειδικά όταν πρέπει να ειδοποιήσετε για την πρόοδο μιας εργασίας παρασκηνίου. Η βιβλιοθήκη έχει ελάχιστο μέγεθος (περίπου 50 KB) και δεν προσθέτει εξαρτήσεις.

Πότε τα LiveData/Flow είναι προτιμότερα

LiveData και Flow αποτελούν μέρος του Android Jetpack και είναι ενσωματωμένα με το Lifecycle. Διαγράφονται αυτόματα κατά την καταστροφή του στοιχείου, εξαλείφοντας τις διαρροές μνήμης. Το Flow υποστηρίζει coroutines και σύνθετους τελεστές μετασχηματισμού. Η Google συνιστά LiveData για το επίπεδο UI και Flow για τα αποθετήρια. Το EventBus παραμένει για διαμονιδικά συμβάντα όπου η πλοήγηση και η επιχειρησιακή λογική δεν ταιριάζουν στο MVVM.

Subscribe και Post: βασικές λειτουργίες

Subscribe — εγγραφή χειριστή συμβάντος μέσω του σχολίου @Subscribe. Η μέθοδος πρέπει να είναι public, void και να δέχεται ακριβώς μία παράμετρο — τον τύπο συμβάντος. Post — αποστολή συμβάντος σε όλους τους εγγεγραμμένους χειριστές μέσω EventBus.getDefault().post(event). Η μέθοδος post δεν επιστρέφει αποτέλεσμα και δεν ενημερώνει πόσοι χειριστές κλήθηκαν. Για συμβάντα με απάντηση, χρησιμοποιήστε ξεχωριστή κλάση Event με πεδίο για το αποτέλεσμα.

Δημιουργία προσαρμοσμένων συμβάντων

Συμβάν — οποιαδήποτε κλάση Java/Kotlin. Συνιστάται η χρήση data class για αμετάβλητα συμβάντα και κανονικής κλάσης για συμβάντα με μεταβλητά πεδία. Η ονομασία συμβάντων πρέπει να αντικατοπτρίζει την ενέργεια: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Αποφύγετε μία κοινή κλάση Event με πεδίο String type — αυτό στερεί τα πλεονεκτήματα της τυποποίησης. Η ιεραρχία συμβάντων (γονικό Event) επιτρέπει την εγγραφή σε μια ομάδα συγγενικών συμβάντων.

kotlin
// Ιεραρχία συμβάντων
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()

// Εγγραφή στη βασική κλάση
class SessionManager {
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onUserEvent(event: UserEvent) {
        when (event) {
            is UserLoggedIn -> startSession(event.userId)
            is UserLoggedOut -> endSession(event.reason)
        }
    }
}

// Αποστολή
EventBus.getDefault().post(UserLoggedIn("user_123"))

Εγγραφή και διαγραφή

Η κλήση EventBus.getDefault().register(this) σαρώνει την κλάση του συνδρομητή μέσω αντανάκλασης ή Subscriber Index και αποθηκεύει τις μεθόδους @Subscribe που βρέθηκαν στο χάρτη συμβάντων. Unregister αφαιρεί τον συνδρομητή από το χάρτη. Εκ νέου εγγραφή χωρίς διαγραφή — σφάλμα (θα εμφανιστεί MultipleSubscriberException). Για Fragment, εγγραφείτε στο onStart() και διαγραφείτε στο onStop(). Για Service — στο onCreate() και onDestroy(). Για ViewModel δεν συνιστάται — χρησιμοποιήστε LiveData.

Sticky Events και ThreadMode

Sticky event — ένα συμβάν που διατηρείται στο EventBus μετά την αποστολή. Οι νέοι συνδρομητές που εγγράφονται μετά το postSticky() λαμβάνουν αμέσως το τελευταίο sticky-συμβάν του αντίστοιχου τύπου. Αυτό είναι βολικό για τη μεταφορά αρχικής κατάστασης: κατά το άνοιγμα μιας οθόνης, λαμβάνει τα τελευταία δεδομένα που στάλθηκαν πριν από την εγγραφή της. Η διαγραφή sticky-συμβάντος γίνεται μέσω EventBus.getDefault().removeStickyEvent(Class).

ThreadMode: τέσσερις τρόποι εκτέλεσης

Το ThreadMode καθορίζει σε ποιο νήμα καλείται ο χειριστής. POSTING (προεπιλογή) — ο χειριστής εκτελείται στο ίδιο νήμα όπου κλήθηκε το post. MAIN — ο χειριστής εκτελείται στο main νήμα μέσω Handler. BACKGROUND — ο χειριστής εκτελείται σε background νήμα· αν το post κλήθηκε σε main νήμα, το EventBus τοποθετεί τον χειριστή στην ουρά background νήματος. ASYNC — κάθε χειριστής εκτελείται σε ξεχωριστό background νήμα από την ομάδα νημάτων. Για ενημερώσεις UI, χρησιμοποιήστε MAIN.

kotlin
// Sticky Event
data class LocationEvent(val lat: Double, val lng: Double)

// Αποστολή sticky-συμβάντος από το LocationService
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))

// Ο συνδρομητής λαμβάνει την τελευταία τοποθεσία αμέσως μετά την εγγραφή
class MapFragment : Fragment() {
    override fun onStart() {
        super.onStart()
        EventBus.getDefault().register(this)
        // Θα λάβει αμέσως LocationEvent αν υπήρχε postSticky
    }

    override fun onStop() {
        EventBus.getDefault().unregister(this)
        super.onStop()
    }

    @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
    fun onLocationEvent(event: LocationEvent) {
        moveMapTo(event.lat, event.lng)
    }
}

// Διαγραφή sticky-συμβάντος
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)

ThreadMode.BACKGROUND vs ASYNC

BACKGROUND χρησιμοποιεί ένα background νήμα για όλους τους χειριστές — εκτελούνται διαδοχικά. ASYNC δημιουργεί ένα νέο νήμα από την ομάδα για κάθε χειριστή — εκτελούνται παράλληλα. Το BACKGROUND είναι κατάλληλο για λειτουργίες εισόδου-εξόδου με κοινή βάση δεδομένων. ASYNC — για ανεξάρτητες μεγάλες λειτουργίες (αιτήματα δικτύου). Και οι δύο τρόποι απαιτούν ασφαλή πρόσβαση νήματος σε κοινόχρηστους πόρους. Αριθμός νημάτων: η ομάδα ASYNC είναι απεριόριστη.

Συνήθη λάθη και απόδοση του EventBus

Κατά τη χρήση του EventBus, οι προγραμματιστές συχνά κάνουν λάθη που οδηγούν σε διαρροές μνήμης, απροσδόκητες κλήσεις και μείωση απόδοσης. Τα πιο κρίσιμα: ξεχασμένη διαγραφή σε Activity, εγγραφή σε onCreate (αντί onStart/onStop), εγγραφή σε Object (όλα τα συμβάντα), αποστολή συμβάντων σε ατελείωτο βρόχο. Η προφίλοποίηση μέσω Android Profiler βοηθά στον εντοπισμό προβλημάτων.

Διαρροές μνήμης μέσω EventBus

Το πιο συνηθισμένο λάθος — εγγραφή Activity στο onCreate() χωρίς διαγραφή στο onDestroy(). Αποτέλεσμα: το EventBus διατηρεί αναφορά στο Activity, ο GC δεν μπορεί να την ελευθερώσει. Κατά την περιστροφή οθόνης, δημιουργείται νέο Activity, το προηγούμενο παραμένει στη μνήμη. Λύση: πάντα να ζευγαρώνετε register/unregister σε onStart/onStop. Για Fragment, χρησιμοποιήστε το ίδιο σχήμα. Εάν το Activity κρατιέται από το EventBus μετά το finish, ελέγξτε μέσω Memory Profiler.

Απόδοση: Subscriber Index

Χωρίς Subscriber Index, το EventBus χρησιμοποιεί αντανάκλαση για την εύρεση μεθόδων @Subscribe σε κάθε register(). Σε συσκευές με Android 6-7, η αντανάκλαση λειτουργεί αργά, προκαλώντας καθυστερήσεις έως 50 ms. Το Subscriber Index εξαλείφει εντελώς την αντανάκλαση: οι μέθοδοι ευρετηριάζονται κατά τη φάση μεταγλώττισης μέσω επεξεργαστή σχολιασμών. Για έργα με 20+ συνδρομητές, το ευρετήριο είναι υποχρεωτικό. Ελέγξτε ότι το kapt ή το annotationProcessor είναι συνδεδεμένο στο build.gradle.

groovy
// build.gradle (app) — σύνδεση Subscriber Index
dependencies {
    implementation 'org.greenrobot:eventbus:3.3.1'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}

// Για Kotlin χρησιμοποιήστε kapt
plugins {
    id 'kotlin-kapt'
}

dependencies {
    implementation 'org.greenrobot:eventbus:3.3.1'
    kapt 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}

// Ρύθμιση ευρετηρίου (στο defaultConfig)
kapt {
    arguments {
        arg('eventBusIndex', 'com.app.EventBusIndex')
    }
}

Εναλλακτικές του EventBus στο σύγχρονο Android

Τα σύγχρονα έργα σε Kotlin και Jetpack Compose προτιμούν SharedFlow και Channel από τη βιβλιοθήκη kotlinx.coroutines. Το SharedFlow υποστηρίζει replay (sticky), buffering και backpressure. Το Channel — εφάπαξ συμβάντα (toast, πλοήγηση). Και οι δύο λύσεις είναι ενσωματωμένες με το Lifecycle μέσω repeatOnLifecycle και δεν απαιτούν χειροκίνητη διαγραφή. Για νέα έργα, συνιστάται SharedFlow αντί EventBus. Για υπάρχοντα έργα, η μετάβαση δικαιολογείται κατά την αναδιάρθρωση.

Συχνές ερωτήσεις

Ποια είναι η διαφορά μεταξύ EventBus και LiveData;

EventBus είναι ένα λεωφορείο συμβάντων για ανταλλαγή δεδομένων μεταξύ οποιωνδήποτε στοιχείων (Activity, Fragment, Service). LiveData είναι ένα περιτύλιγμα με επίγνωση κύκλου ζωής για δεδομένα που παρατηρούνται από ένα στοιχείο UI. Το LiveData διαχειρίζεται αυτόματα την εγγραφή μέσω Lifecycle. Το EventBus απαιτεί χειροκίνητο register/unregister. Το LiveData συνιστάται για το επίπεδο UI, το EventBus — για διαμονιδική επικοινωνία όπου το LiveData δεν είναι βολικό.

Τι είναι το sticky event;

Sticky event — ένα συμβάν που διατηρείται στο EventBus μετά την αποστολή. Οι νέοι συνδρομητές που εγγράφονται μετά το postSticky() λαμβάνουν αμέσως το τελευταίο sticky-συμβάν. Χρησιμοποιείται για αρχική κατάσταση: κατά το άνοιγμα οθόνης, λαμβάνει τα τελευταία δεδομένα χωρίς επαναλαμβανόμενο αίτημα. Διαγράφεται μέσω removeStickyEvent() ή κατά την αποστολή νέου sticky-συμβάντος ίδιου τύπου.

Είναι το EventBus thread-safe;

Ναι, το EventBus είναι thread-safe. Η κλήση post() είναι δυνατή από οποιοδήποτε νήμα. Η παράδοση συμβάντος στους συνδρομητές συγχρονίζεται εντός της βιβλιοθήκης. Το ThreadMode καθορίζει το νήμα εκτέλεσης του χειριστή: MAIN (main νήμα μέσω Handler), POSTING (νήμα αποστολέα), BACKGROUND (ουρά εργασιών παρασκηνίου), ASYNC (ξεχωριστό νήμα). Για ενημερώσεις UI χρησιμοποιήστε MAIN, για βαριές λειτουργίες — ASYNC.

Πώς να κάνετε debug το EventBus;

Ενεργοποιήστε την καταγραφή μέσω EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install(). Εγγραφείτε στο NoSubscriberEvent για παρακολούθηση συμβάντων χωρίς χειριστές. Χρησιμοποιήστε το SubscriberExceptionEvent για καθολικό χειρισμό εξαιρέσεων. Το Android Profiler βοηθά στην εύρεση διαρροών. Για σύνθετα σενάρια, γράψτε ένα τεστ: EventBus.getDefault().register(mock) + post(event) + verify(mock).

Μπορεί να χρησιμοποιηθεί το EventBus σε Kotlin Multiplatform;

Όχι, το EventBus (GreenRobot) είναι συνδεδεμένο με το Android SDK και το JVM. Για Kotlin Multiplatform, χρησιμοποιήστε Kotlin Multiplatform SharedFlow ή KMMBus — βιβλιοθήκες που υποστηρίζουν κοινό κώδικα. Το EventBus στην πλευρά Android ενός έργου KMM λειτουργεί, αλλά δεν είναι διαθέσιμο στο commonMain. Για διαπλατφορμικά συμβάντα, προτιμώνται οι εγγενείς μηχανισμοί πλατφόρμας ή η αφαίρεση μέσω expect/actual.

Σύνοψη

  • EventBus — βιβλιοθήκη Publisher-Subscriber για Android, που υλοποιεί λεωφορείο συμβάντων με τυποποίηση μέσω κλάσεων POJO.
  • Το σχόλιο @Subscribe με παραμέτρους threadMode, sticky, priority καθορίζει τη συμπεριφορά του χειριστή συμβάντος.
  • post() στέλνει το συμβάν σε όλους τους συνδρομητές σύγχρονα· postSticky() διατηρεί το συμβάν για νέους συνδρομητές.
  • ThreadMode διαχειρίζεται το νήμα εκτέλεσης: POSTING (νήμα αποστολέα), MAIN (UI), BACKGROUND (ουρά), ASYNC (ομάδα).
  • Subscriber Index του επεξεργαστή σχολιασμών εξαλείφει την αντανάκλαση και επιταχύνει την εγγραφή.
  • Οι διαρροές μνήμης αποτρέπονται με ζευγοποίηση register/unregister σε onStart/onStop Activity ή Fragment.
  • Για νέα έργα, τα SharedFlow/Channel από kotlinx.coroutines είναι προτιμότερα — είναι lifecycle-aware και thread-safe.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης