EventBus: vad är det, arbetsprincip och Android-händelsebuss

Författare: IT Sectr Publicerad: 2026-03-18 Lästid: 10 min

EventBus är ett bibliotek för Android som implementerar mönstret Publisher-Subscriber via en händelsebuss, vilket möjliggör datautbyte mellan komponenter utan direkt beroende. Utvecklat av GreenRobot, förenklar biblioteket kommunikation mellan Activity, Fragment, Service och Background Thread. Enligt GitHub-data (2025) har EventBus över 25 tusen stjärnor och används i tusentals Android-applikationer. Huvudoperationerna är subscribe (prenumerera på händelse), post (skicka händelse) och sticky event (fördröjd händelse för nya prenumeranter).

Huvudpunkter

  • EventBus — händelsebussbibliotek för löst kopplad kommunikation i Android.
  • @Subscribe — annotering som markerar en metod som hanterare av en händelse av en viss typ.
  • EventBus.getDefault().post() skickar händelsen till alla prenumererade hanterare.
  • Sticky event behåller den senaste händelsen för leverans till nya prenumeranter.
  • ThreadMode bestämmer exekveringstråden för hanteraren: MAIN, POSTING, BACKGROUND, ASYNC.

Vad är EventBus?

EventBus är ett händelsebussbibliotek för Android som implementerar mönstret Publisher-Subscriber (utgivare-prenumerant). Det gör det möjligt att överföra händelser mellan applikationskomponenter (Activity, Fragment, Service, ViewModel) utan att skapa explicita beroenden mellan dem. Till skillnad från standard Android-mekanismer (Intent, BroadcastReceiver) fungerar EventBus inom processen och använder inte IPC. Biblioteket är optimerat för prestanda och använder inte reflektion när Subscriber Index är korrekt konfigurerat.

GreenRobot EventBus: arkitektur

EventBus-arkitekturen består av tre nyckelelement: Event (POJO-klass med data), Subscriber (objekt med metoder markerade med @Subscribe) och EventBus (central dispatcher). Prenumeranten registrerar sig via EventBus.getDefault().register(this), avregistrering — via unregister(this). Händelser är typade: hanterare prenumererar på en specifik händelseklass och anropas endast vid post av en händelse av denna klass eller dess underklasser.

kotlin
// POJO-händelse
data class MessageEvent(
    val message: String,
    val timestamp: Long = System.currentTimeMillis()
)

// Prenumerant i 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
    }
}

// Sändning av händelse från en annan komponent
EventBus.getDefault().post(MessageEvent("Hello from Service"))

Subscriber Index för prestanda

Som standard använder EventBus reflektion för att hitta @Subscribe-metoder vid register(). Subscriber Index genererar ett index över hanterare under kompileringsfasen via en annoteringsprocessor. Detta eliminerar overhead från reflektion och snabbar upp registrering. För att aktivera, lägg till eventbus-annotation-processor i build.gradle. EventBus använder automatiskt indexet om det är tillgängligt i classpath. Utan index fungerar biblioteket fortfarande, men med något lägre prestanda.

Hur fungerar EventBus i Android?

Vid anrop av EventBus.getDefault().post(event) bestämmer biblioteket händelsetypen, hittar alla registrerade prenumeranter med @Subscribe-metoder som accepterar denna typ och anropar dem enligt angiven ThreadMode. Sökning efter prenumeranter utförs via kartan Class → CopyOnWriteArrayList, som byggs upp vid registrering. Om en händelse inte har några prenumeranter avslutas post utan fel — detta är safe-fail-beteende.

Registreringslivscykel

Prenumeranten bör registrera sig i onStart() och avregistrera sig i onStop(). Om den registrerar sig i onCreate() och avregistrerar sig i onDestroy(), kan en Activity som förstörts utan anrop av onDestroy (på grund av finish()) finnas kvar i prenumerantlistan. Prenumerantläcka — ett av huvudproblemen med EventBus: en Activity som finns kvar i prenumerantlistan kommer inte att samlas in av GC förrän den avregistrerar sig. Para alltid ihop register/unregister i rätt lifecycle-metoder.

Prioritet för hanterare

@Subscribe-annoteringen stöder parametern priority (heltal, standard 0). Hanterare med högre prioritet anropas tidigare. cancelEventDelivery() gör det möjligt att avbryta leveransen av händelsen till återstående prenumeranter. Detta är användbart för prioriterade hanterare (loggning, autentisering) som kan avbryta bearbetningen av händelsen av lägre prenumeranter. Funktionen är endast tillgänglig i tråden som skickar händelsen.

kotlin
// Komplext exempel med prioritet
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)
    }
}

// Sändning av händelse
EventBus.getDefault().post(NavigationEvent("profile", bundle))

EventBus vs LocalBroadcastManager vs LiveData

Android erbjuder flera mekanismer för intraprocesskommunikation: EventBus, LocalBroadcastManager (föråldrad) och LiveData/Flow. Var och en har sina fördelar och nackdelar. Valet beror på arkitektonisk strategi och prestandakrav. Googles moderna rekommendationer lutar mot LiveData och Flow på grund av integration med Lifecycle och frånvaro av läckor.

EgenskapEventBusLocalBroadcastManagerLiveData / Flow
TypningVia händelseklassVia Intent filter (String)Via generisk typ
Lifecycle-awareNej (manuell avregistrering)Nej (manuell avregistrering)Ja (automatiskt)
StickyJa (postSticky)NejJa (LiveData — alltid sticky)
ThreadModeMAIN, POSTING, BACKGROUND, ASYNCEndast mainVia observe/observeOn
PrestandaHög (Subscriber Index)Medel (IPC-omslag)Hög (observation)

När EventBus är att föredra

EventBus är användbart i projekt med äldre kod (legacy code) och där LiveData/Flow inte är tillgängliga (Java-only-projekt). EventBus Sticky events ger flexibilitet som saknas i LocalBroadcastManager. EventBus är också enklare för att skicka händelser från Service till Activity utan ViewModel — särskilt när man behöver meddela om framsteg i en bakgrundsuppgift. Biblioteket har en minimal storlek (cirka 50 KB) och lägger inga beroenden.

När LiveData/Flow är att föredra

LiveData och Flow är en del av Android Jetpack och är integrerade med Lifecycle. De avregistrerar sig automatiskt vid komponentförstöring, vilket eliminerar minnesläckor. Flow stöder korutiner och komplexa transformationsoperatorer. Google rekommenderar LiveData för UI-lagret och Flow för datalager. EventBus finns kvar för tvärmodulhändelser där navigering och affärslogik inte passar in i MVVM.

Subscribe och Post: grundläggande operationer

Subscribe — registrering av en händelsehanterare via @Subscribe-annoterigen. Metoden måste vara public, void och acceptera exakt en parameter — händelsetypen. Post — sändning av händelsen till alla prenumererade hanterare via EventBus.getDefault().post(event). Metoden post returnerar inget resultat och meddelar inte hur många hanterare som anropats. För händelser med svar, använd en separat Event-klass med ett fält för resultatet.

Skapa anpassade händelser

En händelse — vilken Java/Kotlin-klass som helst. Det rekommenderas att använda data class för oföränderliga händelser och vanlig klass för händelser med mutable-fält. Namngivning av händelser bör återspegla åtgärden: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Undvik en gemensam Event-klass med ett String type-fält — detta tar bort fördelarna med typning. Händelsehierarki (förälder-Event) möjliggör prenumeration på en grupp relaterade händelser.

kotlin
// Händelsehierarki
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()

// Prenumeration på basklass
class SessionManager {
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onUserEvent(event: UserEvent) {
        when (event) {
            is UserLoggedIn -> startSession(event.userId)
            is UserLoggedOut -> endSession(event.reason)
        }
    }
}

// Sändning
EventBus.getDefault().post(UserLoggedIn("user_123"))

Registrering och avregistrering

Anropet EventBus.getDefault().register(this) skannar prenumerantens klass via reflektion eller Subscriber Index och lagrar de hittade @Subscribe-metoderna i händelsekartan. Unregister tar bort prenumeranten från kartan. Återregistrering utan avregistrering — fel (MultipleSubscriberException uppstår). För Fragment, registrera i onStart() och avregistrera i onStop(). För Service — i onCreate() och onDestroy(). För ViewModel rekommenderas inte — använd LiveData.

Sticky Events och ThreadMode

Sticky event — en händelse som bevaras i EventBus efter sändning. Nya prenumeranter som registrerar sig efter postSticky() får omedelbart den senaste sticky-händelsen av motsvarande typ. Detta är bekvämt för överföring av initialt tillstånd: vid öppning av en skärm får den de senaste data som skickats före dess registrering. Borttagning av sticky-händelse kan göras via EventBus.getDefault().removeStickyEvent(Class).

ThreadMode: fyra exekveringslägen

ThreadMode bestämmer i vilken tråd hanteraren anropas. POSTING (standard) — hanteraren exekveras i samma tråd där post anropades. MAIN — hanteraren exekveras i main-tråden via Handler. BACKGROUND — hanteraren exekveras i en bakgrundstråd; om post anropades i main-tråden placerar EventBus hanteraren i bakgrundstrådens kö. ASYNC — varje hanterare exekveras i en separat bakgrundstråd från trådpoolen. För UI-uppdateringar, använd MAIN.

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

// Sändning av sticky-händelse från LocationService
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))

// Prenumeranten får den senaste platsen omedelbart efter registrering
class MapFragment : Fragment() {
    override fun onStart() {
        super.onStart()
        EventBus.getDefault().register(this)
        // Får omedelbart LocationEvent om postSticky fanns
    }

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

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

// Borttagning av sticky-händelse
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)

ThreadMode.BACKGROUND vs ASYNC

BACKGROUND använder en bakgrundstråd för alla hanterare — de exekveras sekventiellt. ASYNC skapar en ny tråd från poolen för varje hanterare — de exekveras parallellt. BACKGROUND är lämpligt för in-/utdataoperationer med en delad databas. ASYNC — för oberoende långa operationer (nätverksförfrågningar). Båda lägena kräver trådsäker åtkomst till delade resurser. Antal trådar: ASYNC-poolen är obegränsad.

Vanliga fel och prestanda för EventBus

Vid användning av EventBus gör utvecklare ofta misstag som leder till minnesläckor, oväntade anrop och prestandaförsämring. De mest kritiska: glömd avregistrering i Activity, registrering i onCreate (istället för onStart/onStop), prenumeration på Object (alla händelser), sändning av händelser i en oändlig loop. Profilering via Android Profiler hjälper till att identifiera problem.

Minnesläckor via EventBus

Det vanligaste felet — registrering av Activity i onCreate() utan avregistrering i onDestroy(). Resultat: EventBus behåller en referens till Activity, GC kan inte frigöra den. Vid skärmrotation skapas en ny Activity, den föregående finns kvar i minnet. Lösning: para alltid ihop register/unregister i onStart/onStop. För Fragment, använd samma schema. Om Activity hålls kvar av EventBus efter finish, kontrollera via Memory Profiler.

Prestanda: Subscriber Index

Utan Subscriber Index använder EventBus reflektion för att hitta @Subscribe-metoder vid varje register(). På enheter med Android 6-7 fungerar reflektion långsamt, vilket orsakar fördröjningar på upp till 50 ms. Subscriber Index eliminerar reflektion helt: metoder indexeras under kompileringsfasen via en annoteringsprocessor. För projekt med 20+ prenumeranter är indexet obligatoriskt. Kontrollera att kapt eller annotationProcessor är ansluten i build.gradle.

groovy
// build.gradle (app) — anslutning av Subscriber Index
dependencies {
    implementation 'org.greenrobot:eventbus:3.3.1'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}

// Använd kapt för Kotlin
plugins {
    id 'kotlin-kapt'
}

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

// Konfiguration av index (i defaultConfig)
kapt {
    arguments {
        arg('eventBusIndex', 'com.app.EventBusIndex')
    }
}

Alternativ till EventBus i modern Android

Moderna projekt på Kotlin och Jetpack Compose föredrar SharedFlow och Channel från biblioteket kotlinx.coroutines. SharedFlow stöder replay (sticky), buffering och backpressure. Channel — engångshändelser (toast, navigering). Båda lösningarna är integrerade med Lifecycle via repeatOnLifecycle och kräver ingen manuell avregistrering. För nya projekt rekommenderas SharedFlow istället för EventBus. För befintliga projekt är migrering motiverad vid omfaktorering.

Vanliga frågor

Vad är skillnaden mellan EventBus och LiveData?

EventBus är en händelsebuss för datautbyte mellan valfria komponenter (Activity, Fragment, Service). LiveData är ett lifecycle-aware omslag för data som observeras av en UI-komponent. LiveData hanterar automatiskt prenumeration via Lifecycle. EventBus kräver manuellt register/unregister. LiveData rekommenderas för UI-lagret, EventBus — för tvärmodulskommunikation där LiveData är obekvämt.

Vad är sticky event?

Sticky event — en händelse som bevaras i EventBus efter sändning. Nya prenumeranter som registrerar sig efter postSticky() får omedelbart den senaste sticky-händelsen. Används för initialt tillstånd: vid öppning av en skärm får den senaste data utan upprepad begäran. Tas bort via removeStickyEvent() eller vid sändning av en ny sticky-händelse av samma typ.

Är EventBus trådsäker?

Ja, EventBus är trådsäker. Anrop av post() är möjligt från vilken tråd som helst. Leverans av händelse till prenumeranter synkroniseras inom biblioteket. ThreadMode bestämmer hanterarens exekveringstråd: MAIN (main-tråd via Handler), POSTING (avsändartråd), BACKGROUND (kö av bakgrundsuppgifter), ASYNC (separat tråd). För UI-uppdateringar, använd MAIN, för tunga operationer — ASYNC.

Hur felsöker man EventBus?

Aktivera loggning via EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install(). Prenumerera på NoSubscriberEvent för spårning av händelser utan hanterare. Använd SubscriberExceptionEvent för global undantagshantering. Android Profiler hjälper att hitta läckor. För komplexa scenarier, skriv ett test: EventBus.getDefault().register(mock) + post(event) + verify(mock).

Kan EventBus användas i Kotlin Multiplatform?

Nej, EventBus (GreenRobot) är bundet till Android SDK och JVM. För Kotlin Multiplatform, använd Kotlin Multiplatform SharedFlow eller KMMBus — bibliotek som stöder delad kod. EventBus fungerar på Android-sidan av ett KMM-projekt men är inte tillgängligt i commonMain. För plattformsoberoende händelser är nativa plattformsmekanismer eller abstraktion via expect/actual att föredra.

Sammanfattning

  • EventBus — Publisher-Subscriber-bibliotek för Android, som implementerar en händelsebuss med typning via POJO-klasser.
  • Annoteringen @Subscribe med parametrarna threadMode, sticky, priority bestämmer händelsehanterarens beteende.
  • post() skickar händelsen synkront till alla prenumeranter; postSticky() bevarar händelsen för nya prenumeranter.
  • ThreadMode hanterar exekveringstråden: POSTING (avsändartråd), MAIN (UI), BACKGROUND (kö), ASYNC (pool).
  • Annoteringsprocessorns Subscriber Index eliminerar reflektion och snabbar upp registrering.
  • Minnesläckor förhindras genom att para register/unregister i onStart/onStop av Activity eller Fragment.
  • För nya projekt är SharedFlow/Channel från kotlinx.coroutines att föredra — de är lifecycle-aware och trådsäkra.

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också