Mobile Development 10 min read

A Lightweight Event Bus for Android: Simplify Component Communication

This article introduces a lightweight event notification framework for Android, explaining its design, core interfaces, event manager implementation, usage examples with login/logout scenarios, lifecycle considerations, duplicate event checks, and compares it to traditional observer patterns.

Suishouji Tech Team
Suishouji Tech Team
Suishouji Tech Team
A Lightweight Event Bus for Android: Simplify Component Communication

Preface

During development, communication between components often becomes complex, especially across modules or threads, leading to high coupling when using simple parameter passing or callbacks. To decouple event publishing and subscribing, a lightweight event notification mechanism is introduced.

Implementation

Event notification consists of defining events, registering/unregistering observers, and dispatching events. A simple interface and an event manager class are sufficient.

Define Interface

interface Observer {
    fun onEvent(event: Int, vararg args: Any?)
    fun listEvents(): IntArray
}

The interface provides listEvents to return interested events and onEvent to receive the event and optional parameters.

Event Management

object EventManager {
    private val HANDLER = Handler(Looper.getMainLooper())
    private val OBSERVER_ARRAY = SparseArray<LinkedList<Observer>>(16)

    @Synchronized
    fun register(observer: Observer?) {
        observer?.listEvents()?.forEach { event ->
            var observerList = OBSERVER_ARRAY.get(event)
            if (observerList == null) {
                observerList = LinkedList()
                OBSERVER_ARRAY.put(event, observerList)
            }
            if (observer !in observerList) {
                observerList.add(observer)
            }
        }
    }

    @Synchronized
    fun unregister(observer: Observer?) {
        observer?.listEvents()?.forEach { event ->
            OBSERVER_ARRAY.get(event)?.removeLastOccurrence(observer)
        }
    }

    @Synchronized
    fun notify(event: Int, vararg args: Any?) {
        OBSERVER_ARRAY.get(event)?.forEach { observer ->
            HANDLER.post { observer.onEvent(event, *args) }
        }
    }
}

HANDLER dispatches events on the UI thread. OBSERVER_ARRAY maps event IDs to lists of observers, similar to a Map<Integer, LinkedList<Observer>>.

register adds an observer to the lists of its interested events; notify iterates the list for a given event and calls onEvent.

In short, one event can have multiple observers, and one observer can listen to multiple events.

Usage

Example with a simple login/logout scenario:

1. Define Events

object Events {
    const val LOGIN = 1
    const val LOGOUT = 2
}

2. Register/Unregister

abstract class BaseActivity : AppCompatActivity(), Observer {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        EventManager.register(this)
    }
    override fun onDestroy() {
        super.onDestroy()
        EventManager.unregister(this)
    }
    override fun onEvent(event: Int, vararg args: Any?) {}
    override fun listEvents(): IntArray = IntArray(0)
}

Activities or Fragments can register in onCreate and unregister in onDestroy. Subclasses override listEvents to specify interested events.

3. Subscribe and Handle Callbacks

class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // UI setup
    }
    override fun listEvents(): IntArray = intArrayOf(Events.LOGIN, Events.LOGOUT)
    override fun onEvent(event: Int, vararg args: Any?) {
        when (event) {
            Events.LOGIN -> setViews(args[0] as String?)
            Events.LOGOUT -> setViews(null)
        }
    }
    private fun setViews(account: String?) {
        // update UI
    }
}

Since callbacks run on the UI thread, UI updates can be performed directly.

4. Send Events

object AccountManager {
    fun login(account: String, password: String) {
        // process login
        EventManager.notify(Events.LOGIN, account)
    }
    fun logout() {
        // process logout
        EventManager.notify(Events.LOGOUT)
    }
}

Events can be sent with or without additional parameters using vararg for flexibility.

Principle Analysis

The approach resembles the observer pattern but focuses on events rather than observable objects. It follows a publish‑subscribe model where observers subscribe to events, and the manager notifies them when those events occur.

Compared to traditional observer pattern, the event‑listener model emphasizes "things happening" (events) instead of "state changes" of a subject.

Other Matters

Lifecycle

Because the event manager holds static references, observers must unregister before they are garbage‑collected to avoid memory leaks. Static observers need not unregister. Weak references can be used to mitigate leaks:

class WeakObserver(target: Observer) : Observer {
    private val reference = WeakReference(target)
    private val events: IntArray = target.listEvents()
    override fun onEvent(event: Int, vararg args: Any?) {
        reference.get()?.onEvent(event, *args)
    }
    override fun listEvents(): IntArray = events
}

Duplicate Check

When many events are defined, duplicate integer values can cause interference. A unit test can verify uniqueness:

fun testDuplicate() {
    val fields = Events::class.java.declaredFields
    val events = fields.filter { it.type == Int::class.java }
    val eventSet = events.map { it.getInt(Events::class.java) }.toSet()
    Assert.assertEquals(events.size, eventSet.size)
}

If duplicates exist, the test fails (e.g., expected 3 but was 2).

View Events

IDE shortcuts like "Find Usages" help locate all subscribers and senders of a particular event.

Conclusion

The presented lightweight event framework offers clear event management without the extra features of libraries like EventBus (e.g., sticky events, thread selection). It aims to simplify component communication while remaining easy to understand and extend.

AndroidKotlinObserver Patterncomponent communicationevent busevent manager
Suishouji Tech Team
Written by

Suishouji Tech Team

Suishouji's official tech channel, sharing original technical articles, posting recruitment opportunities, and hosting events. Follow us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.