Pseudo caching : retrieve data first from a cache, then a network call (using concat, concatEager, merge or publish)
We have two source Observables: a disk (fast) cache and a network (fresh) call. Typically the disk Observable is much faster than the network Observable. But in order to demonstrate the working, we've also used a fake "slower" disk cache just to see how the operators behave.
This is demonstrated using 4 techniques:
- .concat
- .concatEager
- .merge
- .publishselector + merge + takeUntil
The 4th technique is probably what you want to use eventually but it's interesting to go through the progression of techniques, to understand why.
concatis great. It retrieves information from the first Observable (disk cache in our case) and then the subsequent network Observable. Since the disk cache is presumably faster, all appears well and the disk cache is loaded up fast, and once the network call finishes we swap out the "fresh" results.
The problem withconcatis that the subsequent observable doesn't even start until the first Observable completes. That can be a problem. We want all observables to start simultaneously but produce the results in a way we expect. Thankfully RxJava introducedconcatEagerwhich does exactly that. It starts both observables but buffers the result from the latter one until the former Observable finishes. This is a completely viable option.
Sometimes though, you just want to start showing the results immediately. Assuming the first observable (for some strange reason) takes really long to run through all its items, even if the first few items from the second observable have come down the wire it will forcibly be queued. You don't necessarily want to "wait" on any Observable. In these situations, we could use themergeoperator. It interleaves items as they are emitted. This works great and starts to spit out the results as soon as they're shown.
Similar to theconcatoperator, if your first Observable is always faster than the second Observable you won't run into any problems. However the problem withmergeis: if for some strange reason an item is emitted by the cache or slower observable_after_the newer/fresher observable, it will overwrite the newer content. Click the "MERGE (SLOWER DISK)" button in the example to see this problem in action. @JakeWharton and @swankjesse contributions go to 0! In the real world this could be bad, as it would mean the fresh data would get overridden by stale disk data.
To solve this problem you can use merge in combination with the super niftypublishoperator which takes in a "selector". I wrote about this usage in ablog postbut I haveJedi JWto thank for reminding of this technique. Wepublishthe network observable and provide it a selector which starts emitting from the disk cache, up until the point that the network observable starts emitting. Once the network observable starts emitting, it ignores all results from the disk observable. This is perfect and handles any problems we might have.
Previously, I was using themergeoperator but overcoming the problem of results being overwritten by monitoring the "resultAge". See the oldPseudoCacheMergeFragmentexample if you're curious to see this old implementation.