How to achieve buffering logic with publish sequence in RxJava2?

Viewed 505

I want to achieve the following with RxJava and as I may not have enough knowledge in this area would like to have some help :)

I need to create a PublishSubject which would emit events with the following sequence:

  • Emit 1, 2, 3
  • Buffer 4 in subscribe's completion if a certain condition is not satisfied (may be a network connection for example or some other condition).
  • For 5, 6 ... buffer after 4 if the condition is not satisfied yet.
  • Repeat to emit 4 after some time when the condition is satisfied.
  • If trying to emit 5,6 and the condition is satisfied, then instead of buffering 5, 6 ... after 4, just emit 4 and then 5, 6, 7 , 8 ...

The last 2 points are necessary because the sequence of emitting is really important, which makes difficulties for me to achieve to this.

I hope I could describe what I want to achieve :)

Findings: After asking this question I've done some findings and achieved the following:

private Observable observable = publishSubject
        .observeOn(Schedulers.io())
        .map(Manager::callNew)
        .doOnError(throwable -> Logger.e(throwable, "Error occurred"))
        .retryWhen(throwableObservable -> throwableObservable
                .zipWith(Observable.range(1, 10), (n, i) -> i)
                .flatMap(retryCount -> {
                    long retrySeconds = (long) Math.pow(2, retryCount);
                    Logger.d("The call has been failed retrying in %s seconds. Retry count %s", retrySeconds, retryCount);
                    return Observable.timer(retrySeconds, TimeUnit.SECONDS)
                            .doOnNext(aLong -> {
                                C24Logger.d("Timer was completed. %s", aLong);
                            })
                            .doOnComplete(() -> Logger.d("Timer was completed."));
                }));

The problem is here with PublishSubject. Because it already has emitted all the items, it emits only new ones for retryWhen. If I use ReplaySubject them it emits also the old completed items too for the new retryWhen re-subscribe, which I do not need anymore.

Is there a way to use the ReplaySubject to remove the completed items from the buffer?

1 Answers

You want to be able to turn buffering on and off, depending upon an external condition. Perhaps the simplest way to do it is use the buffer() operator to continually buffer items based on the condition.

(I have removed stuff from the observer chain)

private Observable observable = publishSubject
      .publish( obs -> obs.buffer( obs.filter( v -> externalCondition( v ) ) ) )
      .flatMapIterable( bufferedList -> bufferedList )
      .subscribe( ... );

The publish() operator allows multiple observer chains to subscribe to the incoming observer chain. The buffer() operator monitors the observable that emits a value only when the external condition is true.

When the external condition is true, buffer() will emit a series of lists with only a single element. When the condition goes false, buffer() starts buffering up the results, and when the condition goes true again, all the buffered items are emitted as a list. The flatMapIterable() step will take each item out of the buffer and emit it separately.

Related