Android: Reactive View Part I
This post is the 1st part of the series of blog posts I'm writing about making Android view layer Reactive.
- Android Reactive View Part I ← This Post Is Here!
- Android Reactive View Part II
- Android: Reactive View Part III.
RxJava is not only for threading
A few years back, to make a network call or running a long running process in Android development, we need to use AsyncTask
, but it has it's own drawbacks.
Then, RxJava came along and gained a lot of popularity in Android development to solve this issue.
So when I learned about RxJava, I thought that the library is only used for making background job and switching threads more convenient.
But I was so wrong about it!
The main purpose of RxJava is for making event processing easier.
In this post I want to talk about how to use RxJava to make the View
layer reactive in Android development, instead of just using RxJava just to make network calls like I used to do.
Pre-requisite
This post is targeted for readers who have some experience with RxJava, ViewModel, MVP, MVVM, LiveData, Kotlin, because I will be using a little bit of everything in this post.
Imaginary Spec
Let's pretend we want to create a home page. Where we want to make a page like this:
Description
- has one
button
- when the
button
is clicked, it makes a network call to get morePosts
- the result of network call will populate the
RecyclerView
1. Starting with Simple Network Calls
Let's begin by looking at how to make a simple network call with RxJava, it would look like this:
fun onCreate() {
setContentView(...)
someApi.getMorePosts() // 1. make network call
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { newPosts ->
recyclerView.submitList(newPosts) // 2. show result on screen
}
}
Pretend api.getPosts()
is a network call that returns an Observable
:
interface SomeApi {
fun getMorePosts(): Observable<List<Post>>
}
This is pretty straight forward if you know a little of RxJava.
2. Button click
Next, we make the network call to be triggered by a button click. The MainActivity.kt
would look like this:
fun onCreate() {
setContentView(...)
button.setOnClickListener {
someApi.getMorePosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { newPosts ->
recyclerView.submitList(newPosts)
}
}
}
This is a working implementation, however the view layer is not made reactive yet.
3. MVP Architecture + Non-Reactive View Layer
Before going into making View Layer reactive, let's add a simple MVP implementation, since it's a very common pattern.
In the MVP Architecture, it would look something like this:
class MainActivity: AppCompatActivity(), SomePresenter.View {
val somePresenter = SomePresenter(this)
fun onCreate() {
setContentView(...)
button.setOnClickListener {
somePresenter.onButtonClick()
}
}
override fun updateRecyclerView(newPosts: List<Post>) {
recyclerView.submitList(newPosts)
}
}
class SomePresenter(val view: SomePresenter.View) {
fun onButtonClick() {
someApi.getMorePosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { newPosts ->
view.updateRecyclerView(newPosts)
}
}
interface View {
fun updateRecyclerView(newPosts: List<Post>)
}
}
As you can see in the example, whenever the button
is clicked, somePresenter.onButtonClick()
has to be called manually.
4. MVP Architecture + Reactive View Layer
Next, let's try to make the View Layer reactive. Here's how the button
can be made reactive. 👇
By using the RxBinding library by Jake Wharton, the button
(or any View
type can be made reactive through extension function using clicks()
.
class MainActivity: AppCompatActivity(), SomePresenter.View {
val buttonObservable by lazy { button.clicks() }
val somePresenter by lazy { SomePresenter(this, buttonObservable) }
fun onCreate() {
setContentView(...)
somePresenter.onCreate()
}
override fun updateRecyclerView(newPosts: List<Post>) {
recyclerView.submitList(newPosts)
}
}
Note that after the button
is made to be reactive, the buttonObservable
is passed into the Presenter
through the constructor.
class SomePresenter(val view: SomePresenter.View, val buttonObservable: Observable<Unit>) {
fun onCreate() {
buttonObservable
.flatMap { someApi.getMorePosts() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { newPosts ->
view.updateRecyclerView(newPosts)
}
}
interface View {
fun updateRecyclerView(newPosts: List<Post>)
}
}
Inside the Presenter
, the buttonObservable
can be subscribed
to and used as a trigger to the network call someApi.getMorePosts()
.
5. Discussion
In this trivial example, both methods (non-reactive view vs. reactive view) don't look too different. At this point, the difference is very trival so choosing whichever method seems to be completely up to one's preference. There is however still something interesting to look at.
In the non-reactive flow, the Activity
which is also the View
will have to make the call to presenter.onButtonClick()
.
Let's take a look at the Reactive Flow.
In the reactive flow, the button click is turned into an Observable
, and pass into Presenter
's constructor. It is up to the Presenter
to decide how to subscribe
to this Observable
.
The main purpose of this post is to make a simple comparison between the 2, but without a more complex example, it's hard to see how each one perform.
To keep this post short, I wrote Android: Reactive View Part II.
Tan Jun Rong
Clap to support the author, help others find it, and make your opinion count.