RxSample
RxSample copied to clipboard
Sample project demonstrating how to use RxJava replay
RxSample
This sample demonstrate how to use replay
function from Rx to cache data and prevent new subscrivers execute background operations from the beginning.
App structure
It's built with simple MVP pattern. Our focus is on the model side where Repository
exists. There are 2 views: news list and news details . News list is the one that trigger background operation in the repository. News details is the one that use cached data previously downloaded.
I am using Kodein as dependency injection. The graph is setup inside custom Application class.
How it works
In Repository
we initialize a background request that is downloading news from the web.
var request: ConnectableObservable<List<News>>? = null
override fun getNewsList(): Observable<List<News>> {
if (request == null) {
val localWithSave = localDataSource.getNewsList()
val networkWithSave = remoteDataSource.getNewsList().doOnNext {
localDataSource.storeNewsList(it)
}
request = Observable.concat(localWithSave, networkWithSave).first().replay()
request!!.connect()
}
return request!!
}
The flow
-
Activity
call via presentergetNewsList()
and register itself as subscriber. -
Repositiory
creates new request ifnull
. - Once request is created I call
request.connect()
to start emmiting items. -
Activity
is left and unsubscribe itself from our request but the same request is not stopping. -
Activity
is created and subscribe again toRepository
callinggetNewsList()
via presenter. - Now the most interesting part - all chained operations called before
replay()
are not called again. Insteadreplay
emits to the subscriber all data previously emitted.
Refresh
In case refreshing by user I simply unsubscribe our request and initialize again.
request?.connect { it.unsubscribe() }
MVP
A comment about how I deal with MVP. I don't really like playing ping pong between View
and Presenter
. For me main role of the Presenter
is to mediate between View
and Model
. So if View
trigger some operation it knows what to do about itself but it's Presenter's
responsibility to do something with the rest of the components and only with the rest.
Common use case
-
View
tellPresenter
get me some news -
Presenter
tellView
show indicator -
Presenter
tellModel
get me some news -
Model
tellPresenter
there are your news -
Presenter
tellView
hide indicator -
Presenter
tellView
use that news list or show error
I believe that we make View
very stupid, too much I would say. Since View
is the one that triggers operation, it knows at which point indicator should be shown or when it should be hidden when we get data from the Presenter
.
My way
So I would keep the flow simpler:
-
View
tellPresenter
get me some news -
Presenter
tellModel
get me some news -
Model
tellPresenter
there are your news -
Presenter
tellView
use that news list
Of course I am not saying that Presenter
should never tell View
to show some indicator. It can, in fact it should but only when Model
is triggering some actions. For example, asuming that Presenter
listen to Model
:
-
Model
tellPresenter
I have some extra news for you e.g. from push -
Presenter
tellView
show some indicator because some data is coming -
Presenter
tellView
use that new data -
Presenter
tellView
hide indicator, we are done for now