20

The RxJava v1.0.13 introduced new type of an Observable: rx.Single. It fits great the request-response model but lacks the standard side-effects introducing operators like doOnNext(). So, it's much harder to make multiple things happen as a result.

My idea was to replace doOnNext() with multiple subscriptions to the same Single instance. But this can cause the underlaying work to be done multiple times: once per every subscription.

Example rx.Single implementation:

private class WorkerSubscribe<SomeData>() : Single.OnSubscribe<SomeData> {
    override fun call(sub: SingleSubscriber<in SomeData>) {
        try {
            val result = fetchSomeData()
            sub.onSuccess(result)
        } catch(t: Throwable) {
            sub.onError(t)
        }
    }
}

val single = Single.create<SomeData>(WorkerSubscribe())

Usage:

single.subscribe({}, {})
single.subscribe({}, {})   // Data is fetched for the second time

Is it possible to create a instance of Single that will not fetchSomeData() multiple times even when single.subscribe() is called multiple times, but cache and return the same result?

atok
  • 5,880
  • 3
  • 33
  • 62

5 Answers5

2

You need RxJava Subject: BehaviorSubject or AsyncSubject

Sergey Mashkov
  • 4,630
  • 1
  • 27
  • 24
2

I just needed similar behavior and found some solution.

You can convert Single to Observable apply cache() and then convert it back to Single.

yourSingle.toObservable().cacheWithInitialCapacity(1).toSingle()

I use cacheWithInitialCapacity(1) instead of just cache() as an optimization - Single will never emit more than one item.

 

It is also good idea to provide Transformer implementation

public class SingleUtils {

    public static <T> Single.Transformer<T, T> cached() {
        return single -> single.toObservable()
            .cacheWithInitialCapacity(1)
            .toSingle();
    }
}

so you can use caching wherever you want by calling just

yourSingle.compose(SingleUtils.cached())

Edit: Starting from rxJava 1.2.2 it has been added (https://github.com/ReactiveX/RxJava/releases/tag/v1.2.2)

Implemented exactly this way (https://github.com/ReactiveX/RxJava/pull/4757)

Gawcio
  • 1,125
  • 1
  • 9
  • 17
0

Please check the cache() operator. It's supposed to cache the emissions from the Observable and reproduce them to the subsequent Subscribers.

hotkey
  • 140,743
  • 39
  • 371
  • 326
0

You can create a BehaviorSubject/ReplaySubject/AsyncSubject - and then call toSingle on it.

MrBigglesworth
  • 350
  • 1
  • 8
  • 1
    will you post a code example so that the solution doesn't need to be inferred? – Jayson Minard Jan 06 '16 at 04:35
  • None of those classes have a `toSingle()` method. Though, you can create a `Single.fromObservable()`; which a `Subject` inherits from. – Bryan Mar 01 '17 at 17:04
0

I made workaround, that I'm not happy with it but it works:

public class Network {
   private Object data;
   private Single<Object> dataSingle;

   public Single<Object> getData {
      if (data == null) {
         if (dataSingle == null) {
            dataSingle = Single.create(...)
             .doOnSuccess( data -> this.data = data;)
             .sibscribeOn(..);
         }
         return dataSingle;
      } else {
         return Single.just(data);
      }
   }
}
Eugen Martynov
  • 19,888
  • 10
  • 61
  • 114