{"id":13607920,"url":"https://github.com/adrielcafe/broker","last_synced_at":"2025-10-24T19:40:13.576Z","repository":{"id":149190334,"uuid":"257608373","full_name":"adrielcafe/broker","owner":"adrielcafe","description":"💬 Publish-Subscribe (a.k.a Pub/Sub, EventBus) library for Android and JVM built with Coroutines","archived":false,"fork":false,"pushed_at":"2021-08-14T01:53:19.000Z","size":235,"stargazers_count":97,"open_issues_count":1,"forks_count":9,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T17:51:14.154Z","etag":null,"topics":["android","android-library","broker","event-bus","eventbus","kotlin","kotlin-android","kotlin-coroutines","kotlin-library","message-broker","message-bus","pub-sub","publish-subscribe","publisher-subscriber","pubsub"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adrielcafe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"ko_fi":"adrielcafe"}},"created_at":"2020-04-21T13:41:21.000Z","updated_at":"2025-01-03T10:52:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"7c988ec8-e9b9-4b51-aa16-96d6cac5f957","html_url":"https://github.com/adrielcafe/broker","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/adrielcafe/broker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbroker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbroker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbroker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbroker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adrielcafe","download_url":"https://codeload.github.com/adrielcafe/broker/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbroker/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269928695,"owners_count":24498503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-library","broker","event-bus","eventbus","kotlin","kotlin-android","kotlin-coroutines","kotlin-library","message-broker","message-bus","pub-sub","publish-subscribe","publisher-subscriber","pubsub"],"created_at":"2024-08-01T19:01:22.780Z","updated_at":"2025-10-24T19:40:08.543Z","avatar_url":"https://github.com/adrielcafe.png","language":"Kotlin","funding_links":["https://ko-fi.com/adrielcafe"],"categories":["进程间通信","Kotlin"],"sub_categories":[],"readme":"[![JitPack](https://img.shields.io/jitpack/v/github/adrielcafe/broker.svg?style=for-the-badge)](https://jitpack.io/#adrielcafe/broker) \n[![Android API](https://img.shields.io/badge/api-16%2B-brightgreen.svg?style=for-the-badge)](https://android-arsenal.com/api?level=16) \n[![Github Actions](https://img.shields.io/github/workflow/status/adrielcafe/broker/main/master?style=for-the-badge)](https://github.com/adrielcafe/broker/actions)\n[![Codacy](https://img.shields.io/codacy/grade/ae430c15c7834ac088b23d27e4890dc0.svg?style=for-the-badge)](https://www.codacy.com/app/adriel_cafe/broker) \n[![Codecov](https://img.shields.io/codecov/c/github/adrielcafe/broker/master.svg?style=for-the-badge)](https://codecov.io/gh/adrielcafe/broker) \n[![kotlin](https://img.shields.io/github/languages/top/adrielcafe/broker.svg?style=for-the-badge)](https://kotlinlang.org/) \n[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg?style=for-the-badge)](https://ktlint.github.io/) \n[![License MIT](https://img.shields.io/github/license/adrielcafe/broker.svg?style=for-the-badge\u0026color=yellow)](https://opensource.org/licenses/MIT)  \n\n# Broker\nBroker is a [Publish-Subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) (a.k.a Pub/Sub, EventBus) library for Android and JVM built with [Coroutines](https://github.com/Kotlin/kotlinx.coroutines).\n\n\u003cp align=\"center\"\u003e\n    \u003cimg width=\"80%\" src=\"https://github.com/adrielcafe/broker/raw/master/broker-flow.png?raw=true\"\u003e\n\u003c/p\u003e\n\n```kotlin\nclass MyActivity : AppCompatActivity(), GlobalBroker.Subscriber {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        subscribe\u003cMyEvent\u003e(this) { event -\u003e\n            // Handle event\n        }\n    }\n}\n\nclass MyViewModel : ViewModel(), GlobalBroker.Publisher {\n\n    fun doSomething() {\n        publish(MyEvent(payload))\n    }\n}\n```\n\n**Features**\n* Helps to decouple your code: publishers are loosely coupled to subscribers, and don't even need to know of their existence\n* Works great with Activity, Fragment, Service, Custom View, ViewModel...\n* Provides a [global instance](#global-pubsub) by default and lets you create [your own instances](#local-pubsub)\n* Also provides useful extension functions to avoid boilerplate code\n* [Android Lifecycle-aware](#android-lifecycle-aware): unsubscribe to events automatically\n* [Retained event](#retained-events): cache the last published events\n* Thread-safe: you can publish/subscribe from any thread\n* Fast: all work is done outside the main thread and the events are delivered through a [Coroutines Flow](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)\n* Small: [~30kb](https://jitpack.io/com/github/adrielcafe/broker/broker-core/1.1.0/)\n\n## Usage\nTake a look at the [sample app](https://github.com/adrielcafe/broker/tree/master/sample/src/main/java/cafe/adriel/broker/sample) for working examples.\n\n### Creating events\nEvents (a.k.a Topic, Message) can be represented as `object` (without payload) and `data class` (with payload).\n```kotlin\nobject EventA\ndata class EventB(val message: String)\n```\n\nYou can also group your events inside a `sealed class`, this way you can organize events by module, feature, scope, or similar.\n```kotlin\nsealed class MyEvent {\n    object EventA : MyEvent()\n    data class EventB(val message: String) : MyEvent()\n}\n```\n\n### Global Pub/Sub\nBroker provides a global instance by default with some useful extension functions.\n\nCall `GlobalBroker.subscribe\u003cYourEvent\u003e()` to subscribe to an event and `GlobalBroker.unsubscribe()` to unsubscribe to all events. \n\nTo subscribe, you should pass as parameters:\n* The subscriber (usually the current class but can be a `String`, `Int`, `object`...)\n* A `CoroutineScope` (tip: use the built-in [lifecycleScope and viewModelScope](https://developer.android.com/topic/libraries/architecture/coroutines))\n* An *optional* `CoroutineContext` to run your lambda (default is `Dispatchers.Main`)\n* A `suspend` lambda used to handle the incoming events\n\nCall `subscribe()` in `onStart()` (for Activity and Fragment) and `onAttachedToWindow()` (for Custom View), and call `unsubscribe()` in `onStop()` (for Activity and Fragment) and `onDetachedFromWindow()` (for Custom View).\n```kotlin\nclass MyActivity : AppCompatActivity() {\n\n    override fun onStart() {\n        super.onStart()\n        GlobalBroker.subscribe\u003cMyEvent\u003e(this, lifecycleScope) { event -\u003e\n            // Handle event\n        }\n    }\n\n    override fun onStop() {\n        GlobalBroker.unsubscribe(this)\n        super.onStop()\n    }\n}\n```\n\nTo publish events just call `GlobalBroker.publish()` passing the event as parameter. It can be called from any thread.\n```kotlin\nclass MyViewModel : ViewModel() {\n\n    fun doSomething() {\n        GlobalBroker.publish(MyEvent)\n    }\n}\n```\n\n#### GlobalBroker.Publisher \u0026 GlobalBroker.Subscriber\nYou can avoid some boilerplate code by implementing the `GlobalBroker.Publisher` and `GlobalBroker.Subscriber` interfaces. This also helps to identify the role of your class: is it a Publisher or Subscriber?\n```kotlin\nclass MyActivity : AppCompatActivity(), GlobalBroker.Subscriber {\n\n    override fun onStart() {\n        super.onStart()\n        subscribe\u003cMyEvent\u003e(lifecycleScope) { event -\u003e\n            // Handle event\n        }\n    }\n\n    override fun onStop() {\n        unsubscribe()\n        super.onStop()\n    }\n}\n\nclass MyViewModel : ViewModel(), GlobalBroker.Publisher {\n\n    fun doSomething() {\n        publish(MyEvent)\n    }\n}\n```\n\n### Local Pub/Sub\nIn some situations a global instance is not a good option, because of that you can also create your own Broker instance.\n\nIn the example below, we use [Koin](https://github.com/InsertKoinIO/koin) to inject a Broker instance in the `MyActivity` scope.\n```kotlin\nval myModule = module {\n\n    scope\u003cMyActivity\u003e {\n        scoped { Broker() }\n\n        viewModel { MyViewModel(broker = get()) }\n    }\n}\n```\n\nAnd now we can inject a local Broker instance:\n```kotlin\nclass MyActivity : AppCompatActivity() {\n\n    private val broker by instance\u003cBroker\u003e()\n\n    override fun onStart() {\n        super.onStart()\n        broker.subscribe\u003cMyEvent\u003e(this, lifecycleScope) { event -\u003e\n            // Handle event\n        }\n    }\n\n    override fun onStop() {\n        broker.unsubscribe(this)\n        super.onStop()\n    }\n}\n\nclass MyViewModel(broker: Broker) : ViewModel() {\n\n    fun doSomething() {\n        broker.publish(MyEvent)\n    }\n}\n```\n\n#### BrokerPublisher \u0026 BrokerSubscriber\nBroker class implements two interfaces: `BrokerPublisher` and `BrokerSubscriber`. You can use this to inject only the necessary behavior into your class.\n\nLet's back to the previous example. Instead of provide a Broker instance directly we can provide two injections, one for publishers and another for subscribers.\n```kotlin\nval myModule = module {\n\n    scope\u003cMyActivity\u003e {\n        val broker = Broker()\n\n        scoped\u003cBrokerPublisher\u003e { broker }\n\n        scoped\u003cBrokerSubscriber\u003e { broker }\n\n        viewModel { MyViewModel(broker = get\u003cBrokerPublisher\u003e()) }\n    }\n}\n```\n\nNow we can inject only what our class needs:\n```kotlin\nclass MyActivity : AppCompatActivity() {\n\n    private val broker by instance\u003cBrokerSubscriber\u003e()\n}\n\n\nclass MyViewModel(broker: BrokerPublisher) : ViewModel()\n```\n\n### Android Lifecycle-aware\nBroker's subscribers can be [lifecycle-aware](https://developer.android.com/topic/libraries/architecture/lifecycle)! Works for global and local instances.\n\nInstead of subscribe in `onStart()` and unsubscribe in `onStop()` just subscribe in `onCreate()` and pass the `lifecycleOnwer` as parameter. Your events will now be automatically unsubscribed!\n```kotlin\nclass MyActivity : AppCompatActivity(), GlobalBroker.Subscriber {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        subscribe\u003cMyEvent\u003e(owner = this) { event -\u003e\n            // Handle event\n        }\n    }\n}\n```\n\n### Retained Events\nIt's possible to retain the last published event of a given type. Every time you subscribe to a retained event it will be emitted immediately.\n\nJust use the flags `emitRetained` when subscribing and `retain` when publishing:\n```kotlin\nsubscribe\u003cMyEvent\u003e(this, emitRetained = true) { event -\u003e\n    // Handle event\n}\n\npublish(MyEvent, retain = true)\n```\n\nAt any moment you can query for a retained event (will return null if there are none):\n```kotlin\nval lastEvent = getRetained\u003cSampleEvent\u003e()\n\n// removeRetained() will also return the last retained event\nval lastEvent = removeRetained\u003cSampleEvent\u003e()\n```\n\n### Error handling\nIf the subscriber's lambda throws an error, Broker will catch it and publish the event `BrokerExceptionEvent`. Just subscribe to it if you want to handle the exceptions. \n```kotlin\nsubscribe\u003cBrokerExceptionEvent\u003e(lifecycleScope) { event -\u003e\n    // Handle error\n}\n```\n\n## Import to your project\n1. Add the JitPack repository in your root build.gradle at the end of repositories:\n```gradle\nallprojects {\n    repositories {\n        maven { url 'https://jitpack.io' }\n    }\n}\n```\n\n2. Next, add the desired dependencies to your module:\n```gradle\ndependencies {\n    // Core\n    implementation \"com.github.adrielcafe.broker:broker-core:$currentVersion\"\n\n    // Android Lifecycle support\n    implementation \"com.github.adrielcafe.broker:broker-lifecycle:$currentVersion\"\n}\n```\nCurrent version: [![JitPack](https://img.shields.io/jitpack/v/github/adrielcafe/broker.svg?style=flat-square)](https://jitpack.io/#adrielcafe/broker)\n\n### Platform compatibility\n|         | `broker-core` | `broker-lifecycle` |\n|---------|---------------|--------------------|\n| Android | ✓             | ✓                  |\n| JVM     | ✓             |                    |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrielcafe%2Fbroker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadrielcafe%2Fbroker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrielcafe%2Fbroker/lists"}