Android & Kotlin

RxBus – Implementing event bus with RxJava

Pinterest LinkedIn Tumblr

In this post, I will show you the best practices for Implementing an Event bus with RxJava. The RxBus that I’m going to demonstrate that very helpful for state propagation. So guys, let’s started.

1. Create an Android Project named RxBus Example

    //RxJava
    implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

2. Create a simple RX bus that emits the data to the subscribers using Observable

package com.rxbusexample

import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject

/*
 * Singleton instance
 * A simple rx bus that emits the data to the subscribers using Observable
 */
object RxBus {

    private val publisher = PublishSubject.create<Any>()

    fun publish(event: Any) {
        publisher.onNext(event)
    }

    // Listen should return an Observable and not the publisher
    // Using ofType we filter only events that match that class type
    fun <T> listen(eventType: Class<T>): Observable<T> = publisher.ofType(eventType)

}

3. Create a class named is RxBusEvent

package com.rxbusexample

class RxBusEvent {

    data class ProgressEvent(
        val showDialog: Boolean,
        val message: String? = null
    )

    data class LogOut(val logout: Boolean)

    data class MessageEvent(val message: String)
}

4. How to use this RxBus

package com.rxbusexample

import android.os.CountDownTimer
import android.os.Handler
import java.util.*
import java.util.concurrent.TimeUnit

/**
 * @timer is timer value in milliseconds (suppose 5 min = 5*60*1000)
 * @interval is a time interval of one count ideally it should be 1000
 */
class CountDownTimer(timer: Long, interval: Long) :
    CountDownTimer(timer, interval) {

    companion object {
        private const val SECONDS = 60
    }

    override fun onTick(millisUntilFinished: Long) {

        val textToShow = String.format(
            Locale.getDefault(), "%02d min: %02d sec",
            TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) % SECONDS,
            TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) % SECONDS
        )
        // Publish MessageEvents
        RxBus.publish(RxBusEvent.MessageEvent(textToShow))
    }

    override fun onFinish() {
        // Publish ProgressEvent for showing progressbar
        RxBus.publish(RxBusEvent.ProgressEvent(true, "Logging out ..."))
        Handler().postDelayed({
            // Publish ProgressEvent for dismissing progressbar
            RxBus.publish(RxBusEvent.ProgressEvent(false, "Log out Successfully"))
            RxBus.publish(RxBusEvent.LogOut(true))
        }, 3000)

    }

}

5. Let’s open the main activity layout file and add below

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/textViewCounter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.29"
        android:text="Start" />

    <TextView
        android:id="@+id/textViewMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar"
        tools:text="TextView" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:visibility="gone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textViewCounter" />

    <Button
        android:id="@+id/buttonStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:background="@color/colorAccent"
        android:text="Start"
        android:textColor="@color/colorWhite"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/buttonStop"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/buttonStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:background="@color/colorAccent"
        android:text="Stop"
        android:textColor="@color/colorWhite"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/buttonStart" />

</androidx.constraintlayout.widget.ConstraintLayout>

6. Finally, open the MainActivity and listen to the Observable with event type filter

package com.rxbusexample

import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    companion object {
        const val INTERVAL: Long = 1000
        const val TIMER_TIME: Long = 1 * 60 * 1000
    }

    @SuppressLint("CheckResult")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val counter = CountDownTimer(TIMER_TIME, INTERVAL)

        // set click event on start button
        buttonStart.setOnClickListener { counter.start() }
        // set click event on stop button
        buttonStop.setOnClickListener {
            counter.cancel()
            textViewCounter.text = "Stop"
        }


        // Listen for MessageEvents only
        RxBus.listen(RxBusEvent.MessageEvent::class.java)
            .subscribe { textViewCounter.text = it.message }

        // Listen for ProgressEvent only
        RxBus.listen(RxBusEvent.ProgressEvent::class.java)
            .subscribe {
                textViewMessage.text = it.message
                progressBar.visibility = if (it.showDialog) VISIBLE else GONE
            }


        // Listen for LogOut only
        RxBus.listen(RxBusEvent.LogOut::class.java)
            .subscribe {
                Toast.makeText(this, "Logout Done !", Toast.LENGTH_LONG).show()
            }

        /*
          // Listen for String events only
          RxBus.listen(String::class.java).subscribe({
              println("Im a String event $it")
          })

          // Listen for Int events only
          RxBus.listen(Int::class.java).subscribe({
              println("Im an Int event $it")
          })*/

    }
}

Conclusion

In this android app tutorial, we learned to implement event bus with rxjava. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.

Get Solution Code

Communication between Fragment and Acitivity using RxJava

3 Comments

  1. Guillermo Capurro Reply

    How would you “unsubscribe” from an event with this solution?

Write A Comment