I like the idea of having a single data stream responsible for fetching data, either from the cache or from the network, so I don't have to care where it comes from while I would like to fetch and display it.
I also like the approach to get fresh data from the network once your current data is stale...but unfortunately there is one drawback in the approach of Dan. The client has to know when it's data is stale, and that's not always the case, at least for me. In my application the client always asks the server for fresh data and if the server doesn't has any, it responses with a 304 (nothing changed).
So the use case for the client I would like to achieve is the following:
- fetch data from cache/database and display it
- request data from server in background simultaneously
- if the server has fresh data, persist it in the database and update the UI with the fresh data
- if the server doesn't have fresh data, just do nothing since the data from cache is already displayed
- do this by using a single stream
Since we know already by Dan Lew how to load the data from multiple sources in a single stream, the first thing to achieve what I would like to have from above is to remove the
first()
call to be able to run all source observables after each other:
// Our sources
Observable<Data> cache = getCacheObservable();
Observable<Data> network = getNetworkObservable();
// Retrieve the first source with data
Observable<Data> source = Observable
.concat(cache, network)
.first();
Now the cache and network observables are running both, but as said before after each other and not simultaneously because that's what
concat()
does. Fortunately there is a method of RxJava which also solves this problem and it's called merge()
. It does basically the same as concat()
but in parallel.
Observable<Data> source = Observable
.merge(cache, network);
Since we want to have all this stuff happening in the background but display it int he UI we need to take care of it by using the right schedulers:
Observable.merge(cache.subscribeOn(Schedulers.newThread(),
network.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((data) -> handleShowData(data));
Here it's important to subscribe each of the single observables on a new thread to ensure it will run in parallel. Otherwise if you would subscribe only the resulting observable on a new thread it would run in background but still after each other (like if we would use
concat()
).Conclusion
I think this is a nice and clean way to get data synced on the client side and I hope you think the same. I didn't cover the part of how to store the data in the database, because it's already covered by the post of Dan Lew.
But I have created a small and simple application on github which fulfills all the points from my use case above, so check it out :)