EventBus : qu'est-ce que c'est, principe de fonctionnement et bus d'événements Android

Auteur : IT Sectr Publié le : 2026-03-18 Temps de lecture : 10 min

EventBus est une bibliothèque pour Android implémentant le modèle Publisher-Subscriber via un bus d'événements, permettant d'échanger des données entre composants sans dépendances directes. Développée par GreenRobot, la bibliothèque simplifie la communication entre Activity, Fragment, Service et Background Thread. Selon les données GitHub (2025), EventBus compte plus de 25 000 étoiles et est utilisé dans des milliers d'applications Android. Les principales opérations sont subscribe (abonnement à un événement), post (envoi d'un événement) et sticky event (événement différé pour les nouveaux abonnés).

Points clés

  • EventBus est une bibliothèque de bus d'événements pour une communication faiblement couplée dans Android.
  • @Subscribe est une annotation qui marque une méthode comme gestionnaire d'un type d'événement spécifique.
  • EventBus.getDefault().post() envoie un événement à tous les gestionnaires abonnés.
  • Sticky event conserve le dernier événement pour livraison aux nouveaux abonnés.
  • ThreadMode détermine le thread d'exécution du gestionnaire : MAIN, POSTING, BACKGROUND, ASYNC.

Qu'est-ce qu'EventBus ?

EventBus est une bibliothèque de bus d'événements pour Android implémentant le modèle Publisher-Subscriber. Elle permet de passer des événements entre les composants de l'application (Activity, Fragment, Service, ViewModel) sans créer de dépendances explicites entre eux. Contrairement aux mécanismes Android standard (Intent, BroadcastReceiver), EventBus fonctionne au sein du processus et n'utilise pas d'IPC. La bibliothèque est optimisée pour les performances et n'utilise pas la réflexion lorsque Subscriber Index est correctement configuré.

GreenRobot EventBus : architecture

L'architecture d'EventBus se compose de trois éléments clés : Event (classe POJO avec données), Subscriber (objet avec des méthodes annotées @Subscribe) et EventBus (répartiteur central). L'abonné s'enregistre via EventBus.getDefault().register(this) et se désenregistre via unregister(this). Les événements sont typés : les gestionnaires s'abonnent à une classe d'événement spécifique et ne sont invoqués que lorsqu'un événement de cette classe ou de ses sous-classes est publié.

kotlin
// Événement POJO
data class MessageEvent(
    val message: String,
    val timestamp: Long = System.currentTimeMillis()
)

// Abonné dans 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
    }
}

// Envoi d'événement depuis un autre composant
EventBus.getDefault().post(MessageEvent("Hello from Service"))

Subscriber Index pour les performances

Par défaut, EventBus utilise la réflexion pour trouver les méthodes @Subscribe lors de register(). Subscriber Index génère un index des gestionnaires à la compilation via un processeur d'annotations. Cela élimine la surcharge de la réflexion et accélère l'enregistrement. Pour l'activer, ajoutez eventbus-annotation-processor au build.gradle. EventBus utilise automatiquement l'index s'il est disponible dans le classpath. Sans l'index, la bibliothèque fonctionne toujours, mais avec une légère diminution des performances.

Comment EventBus fonctionne-t-il dans Android ?

Lorsque EventBus.getDefault().post(event) est appelé, la bibliothèque détermine le type d'événement, trouve tous les abonnés enregistrés avec des méthodes @Subscribe acceptant ce type et les invoque selon le ThreadMode spécifié. La recherche d'abonnés est effectuée à l'aide d'une map Class → CopyOnWriteArrayList construite lors de l'enregistrement. Si un événement n'a pas d'abonnés, post se termine sans erreur — c'est un comportement safe-fail.

Cycle de vie de l'enregistrement

Un abonné doit s'enregistrer dans onStart() et se désenregistrer dans onStop(). Si vous vous enregistrez dans onCreate() et vous désenregistrez dans onDestroy(), une Activity détruite sans appel à onDestroy (à cause de finish()) peut rester dans la liste des abonnés. La fuite d'abonné est l'un des principaux problèmes d'EventBus : une Activity restant dans la liste des abonnés ne sera pas libérée par le GC jusqu'à ce qu'elle se désenregistre. Associez toujours register/unregister dans les bonnes méthodes de cycle de vie.

Priorité des gestionnaires

L'annotation @Subscribe prend en charge un paramètre priority (entier, par défaut 0). Les gestionnaires avec une priorité plus élevée sont appelés en premier. cancelEventDelivery() permet d'interrompre la livraison de l'événement aux abonnés restants. Ceci est utile pour les gestionnaires prioritaires (journalisation, authentification) qui peuvent annuler le traitement de l'événement par les abonnés en aval. Cette fonction est disponible uniquement dans le thread d'envoi de l'événement.

kotlin
// Exemple complexe avec priorité
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)
    }
}

// Envoi d'événement
EventBus.getDefault().post(NavigationEvent("profile", bundle))

EventBus vs LocalBroadcastManager vs LiveData

Android propose plusieurs mécanismes pour la communication intra-processus : EventBus, LocalBroadcastManager (obsolète) et LiveData/Flow. Chacun a ses avantages et inconvénients. Le choix dépend de l'approche architecturale et des exigences de performance. Les recommandations modernes de Google penchent vers LiveData et Flow en raison de l'intégration avec Lifecycle et de l'absence de fuites.

CaractéristiqueEventBusLocalBroadcastManagerLiveData / Flow
TypageVia classe d'événementVia filtre Intent (String)Via type générique
Lifecycle-awareNon (désenregistrement manuel)Non (désenregistrement manuel)Oui (automatique)
StickyOui (postSticky)NonOui (LiveData est toujours sticky)
ThreadModeMAIN, POSTING, BACKGROUND, ASYNCMain uniquementVia observe/observeOn
PerformancesÉlevées (Subscriber Index)Moyennes (wrapper IPC)Élevées (observation)

Quand EventBus est préférable

EventBus est utile dans les projets avec beaucoup de code legacy et où LiveData/Flow ne sont pas disponibles (projets Java uniquement). Les sticky events d'EventBus offrent une flexibilité absente de LocalBroadcastManager. EventBus est également plus simple pour envoyer des événements de Service à Activity sans ViewModel — surtout lorsqu'il faut notifier de la progression d'une tâche en arrière-plan. La bibliothèque a une taille minimale (environ 50 Ko) et n'ajoute pas de dépendances.

Quand LiveData/Flow est préférable

LiveData et Flow font partie d'Android Jetpack et sont intégrés à Lifecycle. Ils se désabonnent automatiquement lorsqu'un composant est détruit, éliminant les fuites mémoire. Flow prend en charge les coroutines et les opérateurs de transformation complexes. Google recommande LiveData pour la couche UI et Flow pour les repositories. EventBus reste utile pour les événements inter-modules où la navigation et la logique métier ne s'intègrent pas dans MVVM.

Subscribe et Post : opérations de base

Subscribe est l'enregistrement d'un gestionnaire d'événements via l'annotation @Subscribe. La méthode doit être public, void et accepter exactement un paramètre — le type d'événement. Post est l'envoi d'un événement à tous les gestionnaires abonnés via EventBus.getDefault().post(event). La méthode post ne retourne pas de résultat ni n'indique combien de gestionnaires ont été invoqués. Pour les événements avec réponse, utilisez une classe Event séparée avec un champ de résultat.

Création d'événements personnalisés

Un événement est n'importe quelle classe Java/Kotlin. Il est recommandé d'utiliser data class pour les événements immuables et une classe normale pour les événements avec des champs mutables. Le nom de l'événement doit refléter l'action : UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent. Évitez une seule classe Event générique avec un champ String type — cela élimine les avantages du typage. Une hiérarchie d'événements (Event parent) permet de s'abonner à un groupe d'événements connexes.

kotlin
// Hiérarchie d'événements
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()

// Abonnement à la classe de base
class SessionManager {
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onUserEvent(event: UserEvent) {
        when (event) {
            is UserLoggedIn -> startSession(event.userId)
            is UserLoggedOut -> endSession(event.reason)
        }
    }
}

// Envoi
EventBus.getDefault().post(UserLoggedIn("user_123"))

Enregistrement et désenregistrement

L'appel à EventBus.getDefault().register(this) scanne la classe de l'abonné via la réflexion ou Subscriber Index et stocke les méthodes @Subscribe trouvées dans la map d'événements. Unregister supprime l'abonné de la map. Un ré-enregistrement sans désenregistrement est une erreur (lèvera MultipleSubscriberException). Pour Fragment, enregistrez-vous dans onStart() et désenregistrez-vous dans onStop(). Pour Service, dans onCreate() et onDestroy(). Pour ViewModel, ce n'est pas recommandé — utilisez LiveData.

Sticky Events et ThreadMode

Un sticky event est un événement qui persiste dans EventBus après avoir été envoyé. Les nouveaux abonnés enregistrés après postSticky() reçoivent immédiatement le dernier sticky event du type correspondant. C'est pratique pour passer l'état initial : à l'ouverture d'un écran, il reçoit les dernières données envoyées avant son enregistrement. Vous pouvez supprimer un sticky event via EventBus.getDefault().removeStickyEvent(Class).

ThreadMode : quatre modes d'exécution

ThreadMode détermine dans quel thread le gestionnaire est exécuté. POSTING (par défaut) — le gestionnaire s'exécute dans le même thread où post a été appelé. MAIN — le gestionnaire s'exécute dans le thread principal via Handler. BACKGROUND — le gestionnaire s'exécute dans un thread d'arrière-plan ; si post a été appelé dans le thread principal, EventBus place le gestionnaire dans une file d'attente de thread d'arrière-plan. ASYNC — chaque gestionnaire s'exécute dans un thread d'arrière-plan séparé issu d'un pool de threads. Pour les mises à jour UI, utilisez MAIN.

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

// Envoi de sticky event depuis LocationService
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))

// L'abonné reçoit la dernière position immédiatement après l'enregistrement
class MapFragment : Fragment() {
    override fun onStart() {
        super.onStart()
        EventBus.getDefault().register(this)
        // Recevra immédiatement LocationEvent si postSticky a été appelé
    }

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

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

// Suppression de sticky event
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)

ThreadMode.BACKGROUND vs ASYNC

BACKGROUND utilise un seul thread d'arrière-plan pour tous les gestionnaires — ils s'exécutent séquentiellement. ASYNC crée un nouveau thread du pool pour chaque gestionnaire — ils s'exécutent en parallèle. BACKGROUND convient aux opérations d'E/S avec une base de données partagée. ASYNC est pour les opérations longues indépendantes (requêtes réseau). Les deux modes nécessitent un accès thread-safe aux ressources partagées. Tenez compte du nombre de threads : le pool ASYNC est illimité.

Erreurs courantes et performances d'EventBus

Lors de l'utilisation d'EventBus, les développeurs commettent souvent des erreurs entraînant des fuites mémoire, des invocations inattendues et une dégradation des performances. Les plus critiques : oubli du désenregistrement dans Activity, enregistrement dans onCreate (au lieu de onStart/onStop), abonnement à Object (tous les événements), envoi d'événements dans une boucle infinie. Le profilage avec Android Profiler aide à identifier les problèmes.

Fuites mémoire via EventBus

L'erreur la plus courante est d'enregistrer une Activity dans onCreate() sans se désenregistrer dans onDestroy(). Résultat : EventBus conserve une référence à l'Activity, le GC ne peut pas la libérer. Lors de la rotation de l'écran, une nouvelle Activity est créée tandis que la précédente reste en mémoire. Solution : associez toujours register/unregister dans onStart/onStop. Pour Fragment, utilisez le même modèle. Si une Activity est retenue par EventBus après finish, vérifiez avec Memory Profiler.

Performances : Subscriber Index

Sans Subscriber Index, EventBus utilise la réflexion pour trouver les méthodes @Subscribe à chaque register(). Sur les appareils Android 6-7, la réflexion est lente, provoquant des délais allant jusqu'à 50 ms. Subscriber Index élimine complètement la réflexion : les méthodes sont indexées à la compilation via un processeur d'annotations. Pour les projets avec 20 abonnés ou plus, l'index est obligatoire. Assurez-vous que kapt ou annotationProcessor est configuré dans build.gradle.

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

// Pour Kotlin, utilisez kapt
plugins {
    id 'kotlin-kapt'
}

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

// Configuration de l'index (dans defaultConfig)
kapt {
    arguments {
        arg('eventBusIndex', 'com.app.EventBusIndex')
    }
}

Alternatives à EventBus dans l'Android moderne

Les projets modernes utilisant Kotlin et Jetpack Compose préfèrent SharedFlow et Channel de la bibliothèque kotlinx.coroutines. SharedFlow prend en charge le replay (sticky), le buffering et le backpressure. Channel gère les événements uniques (toast, navigation). Les deux solutions sont intégrées à Lifecycle via repeatOnLifecycle et ne nécessitent pas de désabonnement manuel. Pour les nouveaux projets, SharedFlow est recommandé plutôt qu'EventBus. Pour les projets existants, la migration est justifiée lors du refactoring.

Questions fréquentes

Quelle est la différence entre EventBus et LiveData ?

EventBus est un bus d'événements pour l'échange de données entre tous les composants (Activity, Fragment, Service). LiveData est un wrapper lifecycle-aware pour les données observées par un composant UI. LiveData gère automatiquement l'abonnement via Lifecycle. EventBus nécessite un register/unregister manuel. LiveData est recommandé pour la couche UI, EventBus pour la communication inter-modules où LiveData est peu pratique.

Qu'est-ce qu'un sticky event ?

Un sticky event est un événement qui persiste dans EventBus après avoir été envoyé. Les nouveaux abonnés enregistrés après postSticky() reçoivent immédiatement le dernier sticky event. Il est utilisé pour l'état initial : à l'ouverture d'un écran, il reçoit les dernières données sans nouvelle requête. Il est supprimé via removeStickyEvent() ou lors de l'envoi d'un nouveau sticky event du même type.

EventBus est-il thread-safe ?

Oui, EventBus est thread-safe. L'appel à post() est possible depuis n'importe quel thread. La livraison des événements aux abonnés est synchronisée en interne. ThreadMode détermine le thread d'exécution du gestionnaire : MAIN (thread principal via Handler), POSTING (thread appelant), BACKGROUND (file d'attente de tâches en arrière-plan), ASYNC (thread séparé). Pour les mises à jour UI, utilisez MAIN. Pour les opérations lourdes, utilisez ASYNC.

Comment déboguer EventBus ?

Activez la journalisation via EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install(). Abonnez-vous à NoSubscriberEvent pour suivre les événements sans gestionnaires. Utilisez SubscriberExceptionEvent pour la gestion globale des exceptions. Android Profiler aide à trouver les fuites. Pour les scénarios complexes, écrivez un test : EventBus.getDefault().register(mock) + post(event) + verify(mock).

Peut-on utiliser EventBus dans Kotlin Multiplatform ?

Non, EventBus (GreenRobot) est lié au SDK Android et à la JVM. Pour Kotlin Multiplatform, utilisez Kotlin Multiplatform SharedFlow ou KMMBus — des bibliothèques supportant le code partagé. EventBus fonctionne du côté Android d'un projet KMM mais n'est pas disponible dans commonMain. Pour les événements cross-platform, préférez les mécanismes natifs de la plateforme ou l'abstraction via expect/actual.

Résumé

  • EventBus est une bibliothèque Publisher-Subscriber pour Android implémentant un bus d'événements avec typage via des classes POJO.
  • L'annotation @Subscribe avec les paramètres threadMode, sticky, priority définit le comportement du gestionnaire d'événements.
  • post() envoie un événement à tous les abonnés de manière synchrone ; postSticky() conserve l'événement pour les nouveaux abonnés.
  • ThreadMode contrôle le thread d'exécution : POSTING (thread appelant), MAIN (UI), BACKGROUND (file d'attente), ASYNC (pool).
  • Subscriber Index via le processeur d'annotations élimine la réflexion et accélère l'enregistrement.
  • Les fuites mémoire sont évitées en associant register/unregister dans onStart/onStop d'Activity ou Fragment.
  • Pour les nouveaux projets, SharedFlow/Channel de kotlinx.coroutines sont préférables — ils sont lifecycle-aware et thread-safe.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi