SnowyTracks
SnowyTracks

Reputation: 1985

RxJava Android return cache before network

I am trying to achieve a cache observable returning quickly, then a network request updating the UI once it completes.

Using Rx startWith, works fine if the phone is in signal, however if I put phone in Aeroplane mode, the retrofit web call fails onError so quickly, that my cache value returned from startWith never gets returned, as onError terminates the observable immediately. I also need to know if onError is thrown by the observable for showing to the user. My cache is almost instant, so I would prefer to return the cache before any network call is attempted. Is there a way to do this neatly? or a better way to do this?

public Observable<String> getMyString(String key){
        WebRequestDbEntity myCachedString = mCache.getWebRequest(key);
        String cachedString = myCachedString.getValue();

        return retrofitWebDataStore().getMyString(key)
                .startWith(Observable.just(cachedString))
                .map(networkString -> {
                    mCache.saveRequest(key, networkString);
                    return networkString;
                });
    }

In the presentation layer:

        getMyString(key)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(this::onSuccess, this::onError);

Upvotes: 2

Views: 1218

Answers (2)

SnowyTracks
SnowyTracks

Reputation: 1985

When observing the observable... use observeOn(AndroidSchedulers.mainThread, true) . Where the true is delay errors. This allows the cache observable to return before the network observable throws an error.

getMyString(key)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread(), true)
                    .subscribe(this::onSuccess, this::onError);

Upvotes: 1

yosriz
yosriz

Reputation: 10267

You might need to consider using concat(), with the cache as first Observable, this way you make sure that the cache result (if any) will return before starting the network Observable. something like this:

Observable<String> cacheObservable = Observable.fromCallable(() -> {
    WebRequestDbEntity myCachedString = mCache.getWebRequest(key);
    return myCachedString.getValue();
});
Observable<String> serverObservable =
        retrofitWebDataStore().getMyString(key)
                .doOnNext(networkString -> mCache.saveRequest(key, networkString));

return Observable.concat(cacheObservable, serverObservable);

that way, you make sure to return first cache Object (you didn't treat no cache case in your example, but it can be solved easily with checkin gfor no cache result and filtering, so cache Observable either return result or just complete without nothing), and you will handle it first in presentation logic. afterwards, network request will kick in, if you get result you will save it to cache in doOnNext() (in your example you did it also with result from the cache, not sure if that's what you wanted), and the only error can be resulted from the network request, but will not make your cache not to return value, as its happening after the cache fetching.

Upvotes: 0

Related Questions