简体   繁体   中英

RxJava building cache with observables for android

I'm having a hard time understanding on how to build a cache using RxJava. The idea is that I need to either get data from a memory cache or load from my database (dynamoDb). However, this cache is supposed to be shared across fragments and or threads. So I need to return existing observables that are currently running and not finished. This allows threads to catch up and not do unnecessary work. I'm new to RxJava so this is what I have in mind as a sketch (missing some code for brevity):

public class DBCache<K, T> {
private final ConcurrentHashMap<K, Set<T>> resultCache = new ConcurrentHashMap<>;
private final ConcurrentHashMap<K, Observable<Set<T>>> observableCache = new ConcurrentHashMap<>;

private Observable<Set<T>> getFromCache(final DynamoDbCacheKey<K, T> query) {
    return Observable.create(new Observable.OnSubscribe<Set<T>>() {
        @Override
        public void call(Subscriber<? super Set<T>> subscriber) {
        Set<T> results = resultCache.get(query.getKey());
        if (results != null && results.size() > 0) {
            subscriber.onNext(results);
        }
        subscriber.onCompleted();
        }
    });
}

public Observable<Set<T>> get(final QueryCacheKey<K, T> query){
    Observable<Set<T>> cachedObservable = observableCache.get(query.getKey());
    if (cachedObservable != null) {
        return cachedObservable;
    }
    Observable<Set<T>> observable = Observable
        .concat(getFromCache(query), getFromNetwork(query))
        .first()
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .cache();
    observableCache.putIfAbsent(query.getKey(), observable);
return observable;
} 

private Observable<Set<T>> getFromNetwork(final QueryCacheKey<K, T> query) {
    return Observable.create(new Observable.OnSubscribe<Set<T>>() {
    @Override
    public void call(Subscriber<? super Set<T>> subscriber) {
        try {
            Set<T> results = loadFromDb(query); //omitted
            resultCache.putIfAbsent(query.getKey(), results);
            subscriber.onNext(results);
            subscriber.onCompleted();
            observableCache.remove(query.getKey());
        } catch (Exception exception) {
            subscriber.onError(exception);
        }
    }
    });
} 

}

Is there a better way to achieve this through RxJava (Not interested in caching strategies). Any thoughts?

Here is a simple example of doing caching and retrieving a value once:

public class RxCache<K, V> {

    final ConcurrentHashMap<K, AsyncSubject<V>> cache;

    final Func1<K, Observable<V>> valueGenerator;

    public RxCache(Func1<K, Observable<V>> valueGenerator) {
        this.valueGenerator = valueGenerator;
        this.cache = new ConcurrentHashMap<>();
    }

    public Observable<V> get(K key) {
        AsyncSubject<V> o = cache.get(key);
        if (o != null) {
            return o;
        }

        o = AsyncSubject.create();

        AsyncSubject<V> p = cache.putIfAbsent(key, o);
        if (p != null) {
            return p;
        }

        valueGenerator.call(key).subscribe(o);

        return o;
    }

    public void remove(K key) {
        cache.remove(key);
    }
}

If you have multiple values, replace AsyncSubject with ReplaySubject .

You can apply caching using BehaviorSubject , you create an Observable and then convert it to BehaviorSubject , then subscribe to this Subject to fetch your data.

Now this Subject will react as an Observable , when subscribed to it will give you the latest data fetched, and will emit new data when it is updated.

See more details and implementation here: https://medium.com/@jaerencoathup/repository-pattern-using-rxjava-and-room-4ce79e4ffc5c

Another approach: https://medium.com/@elye.project/rxjava-clean-way-of-prefetching-data-and-use-later-54800f2652d4

Learn more about Subjects with simple examples: https://blog.mindorks.com/understanding-rxjava-subject-publish-replay-behavior-and-async-subject-224d663d452f

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM