2

I've an cold Observable generated out of my control. When subscribed it always produce (if no errors):

onNext()

onCompleted()

I've an helper class like this:

public class Helper {

    private Subscription subscription;
    private ReplaySubject<MyData> subject;
    private Observable<MyData> coldObservable;

    public HelperClass(Observable<MyData> aColdObservable) {
        coldObservable = aColdObservable;
        subject = ReplaySubject.create(1);
        triggerRefresh();
    }

    public Observable<MyData> getObservable() {
        return subject.asObservable();
    }

    public void triggerRefresh() {
        if (subscription != null) {
            subscription.unsubscribe();
            subscription = null;
        }
        subscription = coldObservable
                              .subscribeOn(Schedulers.io())
                              .observeOn(Schedulers.computation())
                              .subscribe(subject);
    }
}

I've multiple subscriptions to the subject, take this pseudo-code for client:

public class Client {

    private final Helper helper;
    private Observable<MyData> proxiedObservable;

    public Client(Observable<MyData> coldObservable) {
        helper = new Helper(coldObservable);
    }

    public void init() {
        proxiedObservable = helper.getObservable()
                                                        .subscribeOn(Schedulers.io)
                                                        .observeOn(Schedulers.computation());

        Subscription a = newSubscription("A");
        Subscription b = newSubscription("B");
        Subscription c = newSubscription("C");
    }

    public Subscription newSubscription(String name) {
        return proxiedObservable.subscribe(data -> log("next " + name),
                                          throwable -> log("error " + name),
                                          () -> log("complete " + name));
    }

    public void refresh() {
        helper.triggerRefresh();
    }
}

at initialization this is printed in the log:

next A
complete A
next B
complete B
next C
complete C

at some point after that refresh() is called and I would like this exact same log to be repeated, instead nothing is printed.

Apparently when the complete event is fired by the cold observer it automatically unsubscribe all the subscription to my proxiedObservable.

I do not need the complete event, but I DO need the new MyData to reach all the subscriptions.

Is there a way to suppress the onComplete event? Or is there another way to achieve what I need here?

Community
  • 1
  • 1
Daniele Segato
  • 12,314
  • 6
  • 62
  • 88

2 Answers2

6

This problem is what RxRelay set out to solve - a Subject can receive onComplete which causes it to shut down.

If you're not comfortable adding this dependency, then alternatively you could use use materialize() + filter() to remove onComplete from the sequence.

Dan Lew
  • 85,990
  • 32
  • 182
  • 176
  • insta-accept answer because I trust you, thanks I'm gonna look at RxRelay and materialize/filter in the next hour. I'll drop a comment if there's something I do not understand – Daniele Segato Apr 20 '16 at 12:59
  • 1
    I've been trying to use RxRelay. The example do `relay.call(data)`, but I have an Observable as input. How do I pass it? Do I have to place a subscription in the middle? How do I let the error pass? I also tried materialize. I wrote a `materialize().filter().dematerialize()` chain filtering out oncomplete events (the chain is in a method I call with `compose()`) but I keep getting the onComplete. – Daniele Segato Apr 20 '16 at 14:25
  • oh, I got how to use RxRelay: as an action: `coldObservable.subscribe(relay)`. This works but now I don't know how to handle errors, I still want errors to reach my observer, I just don't want the error to complete the sequence. – Daniele Segato Apr 20 '16 at 14:41
  • If you allow errors to reach your `Observer`, then you're going to run into the same problem as you do with completed notifications, since that also terminates the sequence. Generally it's better to return known error states in onNext (so as to avoid sequence termination). – Dan Lew Apr 20 '16 at 15:04
  • Oh, that's a shame. It would have been so useful. Then I guess I need to find another way of dealing with it :/ – Daniele Segato Apr 20 '16 at 15:07
  • This question was a more specific version of [this other](http://stackoverflow.com/questions/36446858/how-to-wrap-a-cold-observable-reactivex-rxjava-that-always-return-the-last-r) and in my case I need to keep filtering / mapping and in general apply operators to my Observable in different places. – Daniele Segato Apr 20 '16 at 15:09
  • I'm discussing this with Jake Wharton here: https://github.com/JakeWharton/RxRelay/issues/8 – Daniele Segato Apr 20 '16 at 16:10
  • sorry I unaccepted the answer, this still doesn't solve the problem for me since I also need error propagated – Daniele Segato Apr 20 '16 at 16:41
  • If you want a Subject/Relay that can emit `onError` then I still stand by my previous comment: you need to wrap the type so that you can push out the state w/ success or failure into `onNext`. `onError` is a terminal event otherwise. – Dan Lew Apr 21 '16 at 11:28
  • Is there an easy way to do it? I've managed by using a PublishRelay to mask the multiple retrofit calls, 3 BehaviorSubject (value, error, loading state), values comes from the Relay, Errors are pushed manually and loading state is handled with a variable and a push on the subject to let it know when it started to load. All is combined with combineLatest + a throttle of 200 ms... seems pretty convoluted to me :) – Daniele Segato Apr 21 '16 at 15:28
  • maybe using materialize and scan I can obtain the same thing without all that code :) – Daniele Segato Apr 21 '16 at 16:23
  • Hey @DanielLew in the RxRelay project Jakewharton making it clear that: --"As more of your code moves to reactive, the need for Subjects and Relays should diminish." so do you know how to replace the RxRelay then? – Kailash Dabhi Dec 04 '16 at 17:51
2

A simple way to suppress onComplete, while still allowing errors to propagate, is this:

concatWith(Observable.never())
Graham Borland
  • 60,055
  • 21
  • 138
  • 179