Since becoming an officially supported language for Android development, Kotlin has rapidly grown in popularity amongst Android developers, with Google reporting a 6x increase in the applications that are created using Kotlin.
If you’ve previously used RxJava or RxAndroid and want to make the switch to Kotlin, or want to start reactive programming with Kotlin, this tutorial is for you. We’ll cover the essentials of creating RxJava 2.0 Observers
, Observables
and data streams in Kotlin, before looking at how you can trim a tonne of boilerplate code from your projects, by combining RxJava with Kotlin extension functions.
Using RxJava with Kotlin can help you create highly reactive apps in less code, but no programming language is perfect, so I’ll also be sharing a workaround for the SAM conversion problem that many developers encounter when they first start using RxJava 2.0 with Kotlin.
To wrap things up, we’ll create an application that demonstrates how you might use RxJava to solve some of the issues you encounter in real-life Android projects.
If this is your first taste of RxJava, then along the way I’ll also be providing all the background information you need to understand the core RxJava concepts. Even if you’ve never experimented with RxJava before, by the end of this article you’ll have a solid understanding of how to use this library in your projects, and you’ll have created several working apps, using RxJava, RxKotlin, RxAndroid, and RxBinding.
What Is RxJava, Anyway?
RxJava is an open-source implementation of the ReactiveX library that helps you create applications in the reactive programming style. Although RxJava is designed to process synchronous and asynchronous streams of data, it isn’t restricted to “traditional” data types. RxJava’s definition of “data” is pretty broad and includes things like caches, variables, properties, and even user input events such as clicks and swipes. Just because your application doesn’t deal with huge numbers or perform complex data transformations, it doesn’t mean that it can’t benefit from RxJava!
For a little background on using RxJava for Android apps, you can check out some of my other posts here on Envato Tuts+.
-
Android SDKGet Started With RxJava 2 for Android
-
Android SDKRxJava 2 for Android Apps: RxBinding and RxLifecycle
So how does RxJava work?
RxJava extends the Observer software design pattern, which is based around the concept of Observers and Observables. To create a basic RxJava data pipeline, you need to:
- Create an Observable.
- Give the Observable some data to emit.
- Create an Observer.
- Subscribe the Observer to the Observable.
As soon as the Observable has at least one Observer, it’ll start emitting data. Every time the Observable emits a piece of data, it’ll notify its assigned Observer by calling the onNext()
method, and the Observer will then typically perform some action in response to this data emission. Once the Observable has finished emitting data, it’ll notify the Observer by calling onComplete()
. The Observable will then terminate, and the data stream will end.
If an exception occurs, then onError()
will be called, and the Observable will terminate immediately without emitting any more data or calling onComplete()
.
But RxJava isn’t just about passing data from an Observable to an Observer! RxJava has a huge collection of operators that you can use to filter, merge, and transform this data. For example, imagine your app has a Pay Now button that detects onClick
events, and you’re worried that an impatient user might tap the button multiple times, causing your app to process several payments.
RxJava lets you transform these onClick
events into a data stream, which you can then manipulate using RxJava’s various operators. In this particular example, you could use the debounce()
operator to filter data emissions that happen in quick succession, so even if the user bashes away at the Pay Now button, your app will only ever register a single payment.
What Are the Benefits to Using RxJava?
We’ve seen how RxJava can help you solve a specific problem, in a specific application, but what does it have to offer Android projects, in general?
RxJava can help simplify your code by giving you a way to write what you want to achieve, rather than writing a list of instructions that your application has to work through. For example, if you wanted to ignore all data emissions that happen within the same 500-millisecond period, then you’d write:
.debounce(500, TimeUnit.MILLISECONDS)
In addition, since RxJava treats almost everything as data, it provides a template that you can apply to a wide range of events: create an Observable, create an Observer, subscribe the Observer to the Observable, rinse and repeat. This formulaic approach results in much more straightforward, human-readable code.
The other major benefit for Android developers is that RxJava can take much of the pain out of multithreading on Android. Today’s mobile users expect their applications to be able to multitask, even if it’s something as simple as downloading data in the background while remaining responsive to user input.
Android has several built-in solutions for creating and managing multiple threads, but none of these are particularly easy to implement, and they can quickly result in complex, verbose code that’s difficult to read and prone to errors.
In RxJava, you create and manage additional threads using a combination of operators and schedulers. You can easily change the thread where work is performed, using the subscribeOn
operator plus a scheduler. For example, here we’re scheduling work to be performed on a new thread:
.subscribeOn(Schedulers.newThread())
You can specify where the results of this work should be posted, using the observeOn
operator. Here, we’re posting the results to Android’s all-important main UI thread, using the AndroidSchedulers.mainThread
scheduler, which is available as part of the RxAndroid library:
.observeOn(AndroidSchedulers.mainThread())
Compared to Android’s built-in multithreading solutions, RxJava’s approach is much more concise and easier to understand.
Again, you can learn more about how RxJava works, and the benefits of adding this library to your project, in my Get Started With RxJava 2 for Android article.
Should I Be Using RxJava or RxKotlin?
Since Kotlin is 100% interoperable with Java, you can use most Java libraries in your Kotlin projects without any difficulties—and the RxJava library is no exception.
There is a dedicated RxKotlin library, which is a Kotlin wrapper around the regular RxJava library. This wrapper provides extensions that optimize RxJava for the Kotlin environment and can further reduce the amount of boilerplate code you need to write.
Since you can use RxJava in Kotlin without ever needing RxKotlin, we’ll be using RxJava throughout this article, unless stated otherwise.
Creating Simple Observers and Observables in Kotlin
Observers and Observables are the building blocks of RxJava, so let’s start by creating:
- A simple Observable that emits a short stream of data in response to a button click event.
- An Observable that reacts to this data by printing different messages to Android Studio’s Logcat.
Create a new project with the settings of your choice, but make sure you select the Include Kotlin support checkbox when prompted. Next, open your project’s build.gradle file and add the RxJava library as a project dependency:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.0.0-alpha1' implementation 'androidx.constraintlayout:constraintlayout:1.1.0' implementation 'io.reactivex.rxjava2:rxjava:2.1.9' }
Then, open your project’s activity_main.xml file and add the button that’ll start the data stream:
There are several different ways to create an Observable, but one of the easiest is to use the just()
operator to convert an object or list of objects into an Observable.
In the following code, we’re creating an Observable (myObservable
) and giving it the items 1, 2, 3, 4, and 5 to emit. We’re also creating an Observer (myObserver
), subscribing it to myObservable
, and then telling it to print a message to Logcat every time it receives a new emission.
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import io.reactivex.Observable import io.reactivex.Observer import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private var TAG = "MainActivity" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //Start the stream when the button is clicked// button.setOnClickListener { startRStream() } } private fun startRStream() { //Create an Observable// val myObservable = getObservable() //Create an Observer// val myObserver = getObserver() //Subscribe myObserver to myObservable// myObservable .subscribe(myObserver) } private fun getObserver(): Observer{ return object : Observer { override fun onSubscribe(d: Disposable) { } //Every time onNext is called, print the value to Android Studio’s Logcat// override fun onNext(s: String) { Log.d(TAG, "onNext: $s") } //Called if an exception is thrown// override fun onError(e: Throwable) { Log.e(TAG, "onError: " + e.message) } //When onComplete is called, print the following to Logcat// override fun onComplete() { Log.d(TAG, "onComplete") } } } //Give myObservable some data to emit// private fun getObservable(): Observable { return Observable.just("1", "2", "3", "4", "5") } }
You can now put this application to the test:
- Install your project on a physical Android smartphone or tablet, or an Android Virtual Device (AVD).
- Give the Start RxJava stream button a click.
- Open Android Studio’s Logcat Monitor by selecting the Android Monitor tab (where the cursor is positioned in the following screenshot) and then selecting the Logcat tab.
At this point, the Observable will start emitting its data, and the Observer will print its messages to Logcat. Your Logcat output should look something like this:
You can download this project from GitHub if you want to try it for yourself.
Kotlin Extensions for RxJava
Now that we’ve seen how to set up a simple RxJava pipeline in Kotlin, let’s look at how you can achieve this in less code, using RxKotlin’s extension functions.
To use the RxKotlin library, you need to add it as a project dependency:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.0.0-alpha1' implementation 'androidx.constraintlayout:constraintlayout:1.1.0' implementation 'io.reactivex.rxjava2:rxjava:2.1.9' //Add the following// implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0' }
In the following example, we’re using RxKotlin’s toObservable()
extension function to transform a List
into an Observable. We’re also using the subscribeBy()
extension function, as it allows us to construct an Observer using named arguments, which results in clearer code.
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.toObservable import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //Start the stream when the button is clicked// button.setOnClickListener { startRStream() } } private fun startRStream() { val list = listOf("1", "2", "3", "4", "5") //Apply the toObservable() extension function// list.toObservable() //Construct your Observer using the subscribeBy() extension function// .subscribeBy( onNext = { println(it) }, onError = { it.printStackTrace() }, onComplete = { println("onComplete!") } ) } }
Here is the output that you should see:
Solving RxJava’s SAM Ambiguity Issue
RxKotlin also provides an important workaround for the SAM conversion issue that can occur when there are multiple SAM parameter overloads on a given Java method. This SAM ambiguity confuses the Kotlin compiler, as it cannot work out which interface it’s supposed to convert, and your project will fail to compile as a result.
This SAM ambiguity is a particular problem when using RxJava 2.0 with Kotlin, as many of the RxJava operators take multiple SAM-compatible types.
Let’s look at the SAM conversion problem in action. In the following code, we’re using the zip()
operator to combine the output of two Observables:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import io.reactivex.Observable import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //Start the stream when the button is clicked// button.setOnClickListener { startRStream() } } private fun startRStream() { val numbers = Observable.range(1, 6) val strings = Observable.just("One", "Two", "Three", "Four", "Five", "Six" ) val zipped = Observable.zip(strings, numbers) { s, n -> "$s $n" } zipped.subscribe(::println) } }
This will cause the Kotlin compiler to throw a type inference error. However, RxKotlin provides helper methods and extension functions for the affected operators, including Observables.zip()
, which we’re using in the following code:
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import io.reactivex.Observable import io.reactivex.rxkotlin.Observables import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //Start the stream when the button is clicked// button.setOnClickListener { startRStream() } } private fun startRStream() { val numbers = Observable.range(1, 6) val strings = Observable.just("One", "Two", "Three", "Four", "Five", "Six" ) val zipped = Observables.zip(strings, numbers) { s, n -> "$s $n" } zipped.subscribe(::println) } }
Here’s the output of this code:
Conclusion
In this tutorial, I showed you how to start using the RxJava library in your Kotlin projects, including using a number of additional supporting libraries, such as RxKotlin and RxBinding. We looked at how you can create simple Observers and Observables in Kotlin, right through to optimizing RxJava for the Kotlin platform, using extension functions.
So far, we’ve used RxJava to create simple Observables that emit data, and Observers that print this data to Android Studio’s Logcat—but this isn’t how you’ll use RxJava in the real world!
In the next post, we’re going to look at how RxJava can help solve real-world problems you’ll encounter when developing Android applications. We’ll use RxJava with Kotlin to create a classic Sign Up screen.
Powered by WPeMatico