This sample demonstrate how to use replay function from Rx to cache data and prevent new subscrivers execute background operations from the beginning.
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.
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!!
}Activitycall via presentergetNewsList()and register itself as subscriber.Repositiorycreates new request ifnull.- Once request is created I call
request.connect()to start emmiting items. Activityis left and unsubscribe itself from our request but the same request is not stopping.Activityis created and subscribe again toRepositorycallinggetNewsList()via presenter.- Now the most interesting part - all chained operations called before
replay()are not called again. Insteadreplayemits to the subscriber all data previously emitted.
In case refreshing by user I simply unsubscribe our request and initialize again.
request?.connect { it.unsubscribe() }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.
ViewtellPresenterget me some newsPresentertellViewshow indicatorPresentertellModelget me some newsModeltellPresenterthere are your newsPresentertellViewhide indicatorPresentertellViewuse 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.
So I would keep the flow simpler:
ViewtellPresenterget me some newsPresentertellModelget me some newsModeltellPresenterthere are your newsPresentertellViewuse 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:
ModeltellPresenterI have some extra news for you e.g. from pushPresentertellViewshow some indicator because some data is comingPresentertellViewuse that new dataPresentertellViewhide indicator, we are done for now