52

When to call the subscribeWith method rather than plain subscribe? And what is the use case?

compositeDisposable.add(get()
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe(this::handleResponse, this::handleError));

VS

   compositeDisposable.add(get()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
              //  .subscribe(this::handleResponse, this::handleError);
                .subscribeWith(new DisposableObserver<News>() {
                    @Override public void onNext(News value) {
                        handleResponse(value);
                    }

                    @Override public void onError(Throwable e) {
                        handleError(e);
                    }

                    @Override public void onComplete() {
                       // dispose here ? why? when the whole thing will get disposed later
                       //via  compositeDisposable.dispose();  in onDestroy();
                    }
                }));

Thank you


Added Later

According to the documentation, both return disposable SingleObserver instances:

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final <E extends SingleObserver<? super T>> E subscribeWith(E observer) {
    subscribe(observer);
    return observer;
}

@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(final Consumer<? super T> onSuccess, final Consumer<? super Throwable> onError) {
    ObjectHelper.requireNonNull(onSuccess, "onSuccess is null");
    ObjectHelper.requireNonNull(onError, "onError is null");
    ConsumerSingleObserver<T> s = new ConsumerSingleObserver<T>(onSuccess, onError);
    subscribe(s);
    return s;
}

Where ConsumerSingleObserver class implements SingleObserver and Disposable.

Incinerator
  • 2,589
  • 3
  • 23
  • 30
bastami82
  • 5,955
  • 7
  • 33
  • 44
  • 2
    Thanks to @Simbatrons answer, to sum up the specefic use case (from what I underestood) is if you have a same observer that you want to bind to different observable, use subscribeWith. (so multiple Observables can use the same observer implementation). please add your comment if you think this is not the only differnce in use case – bastami82 Jul 15 '17 at 08:03
  • 1
    I wonder the same thing – in my opinion your first snippet is way cleaner with the use of lambdas etc. So yeah, it seems like the rare case when you want to reuse the same Observer is the only time you need .subscribeWith()? It's weird that the docs don't really mention the overloaded variant of subscribe that returns the Disposable though. Instead they just point you to use the new and unwieldy subscribeWith() – Incinerator Oct 11 '17 at 12:35

1 Answers1

41

Observable#subscribe explanation:

In your first code snippet:

.subscribe(this::handleResponse, this::handleError));

You are actually using one of the several overloaded Observable#subscribe methods:

public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError)

There is another one that also takes in an Action to perform onComplete:

public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError,
        Action onComplete) {

And another option allows you to simply pass in an Observer (NOTE: void method) (Edit 2 - this method is defined in ObservableSource, which is the interface that Observable extends.)

public final void subscribe(Observer<? super T> observer)

In the second code snippet in your question, you used the subscribeWith method which simply returns the Observer you passed in (for convenience/caching etc):

public final <E extends Observer<? super T>> E subscribeWith(E observer)

Observer#onComplete explanation:

Observer#onComplete gets called after the Observable has emitted all the items in the stream. From the java doc:

/**
 * Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
 * <p>
 * The {@link Observable} will not call this method if it calls {@link #onError}.
 */
void onComplete();

So for example, if the get() in your code snippets returned an Observable that emitted multiple News objects, each one will be handled in the Observer#onNext. Here you can process each item.

After they have all been processed (and assuming no error occured), then the onComplete will get called. Here you can perform any extra actions that you need to do (for eg. update UI) knowing that you've processed all the News objects.

This is not to be confused with Disposable#dispose which gets invoked when the observable stream ends (complete/error), or manually by you to terminate the observation (this is where the CompositeDisposable comes in as it helps you dispose of all your Disposables that it contains at once).

If in your scenario the get() will return an Observable that only emits a single item, then instead of using an Observable, consider using an io.reactivex.Single where you only process the one item (in onSuccess), and won't need to specify an Action for onComplete :)

Edit: response to your comment:

However I still do not get use of subscribeWith, you said it passes the observer for caching etc , where does it pass to? on complete? and from what I understood subscribeWith is not actually consuming the observable( or Single) right?

To further clarify the subscribeWith explanation, what I meant was that it will consume the Observer object that you passed into the subscribeWith (exactly like the subscribe method) however it will additionally return that same Observer right back to you. At time of writing, the implementation of subscribeWith is:

public final <E extends Observer<? super T>> E subscribeWith(E observer) {
    subscribe(observer);
    return observer;
}

Therefore, subscribeWith can be used interchangeably with subscribe.

Can you give a use case of subscribeWith with example? I guess that will answer the question completely

The subscribeWith javadoc gives the following usage example:

Observable<Integer> source = Observable.range(1, 10);
CompositeDisposable composite = new CompositeDisposable();

ResourceObserver<Integer> rs = new ResourceObserver<>() {
     // ...
};

composite.add(source.subscribeWith(rs));

See here the usage of subscribeWith will return that same ResourceObserver object that was instantiated. This gives the convenience of performing the subscription & adding the ResourceObserver to the CompositeDisposable in one line (note that ResourceObservable implements Disposable.)

Edit 2 Replying to second comment.

source.subscribeWith(rs); source.subscribe(rs); both return SingleObserver instance,

ObservableSource#subscribe(Observer <? super T> observer) does NOT return an Observer. It is a void method (See NOTE under the Observable#subscribe explanation above.) Whereas the Observable#subscribeWith DOES return the Observer. If we were to rewrite the example usage code using ObservableSource#subscribe instead, we'd have to do it in two lines like so:

source.subscribe(rs); //ObservableSource#subscribe is a void method so nothing will be returned
composite.add(rs);

Whereas the Observable#subscribeWith method made it convenient for us to do the above in just one line composite.add(source.subscribeWith(rs));

It can get confusing with all the overloaded subscribe methods that look somewhat similar, however there are differences (some of which are subtle). Looking at the code and documentation helps to provide the distinction between them.


Edit 3 Another sample use case for subscribeWith

The subscribeWith method is useful for when you have a specific implementation of an Observer that you may want to reuse. For example, in the sample code above, it provided a specific implementation of ResourceObserver in the subscription, thereby inheriting it's functionality while still allowing you to handle onNext onError and onComplete.

Another example use: for to the sample code in your question, what if you wanted to perform the same subscription for the get() response in multiple places?

Instead of copying the Consumer implementations for onNext and onError across different classes, what you can do instead is define a new class for eg.

//sample code..
public class GetNewsObserver extends DisposableObserver<News> {
    //implement your onNext, onError, onComplete.
    ....
}

Now, whenever you do that get() request, you can simply subscribe by doing:

compositeDisposable.add(get()
    ...
    .subscribeWith(new GetNewsObserver()));

See the code is simple now, you maintain separation of responsibility for handling the response, and can now reuse that GetNewsObserver wherever you want.

mmaarouf
  • 560
  • 6
  • 9
  • 1
    thanks for your valuable info, it helped me to have a better understanding on disposing and onComplete. However I still do not get use of subscribeWith, you said it passes the observer for caching etc , where does it pass to? on complete? and from what I understood subscribeWith is not actually consuming the observable( or Single) right? So subscribeWith can not be used interchangeably with Subscribe method. Can you give a use case of subscribeWith with example? I guess that will answer the question completely. tnx – bastami82 Jul 14 '17 at 07:51
  • 1
    Added response to the answer :) – mmaarouf Jul 14 '17 at 15:29
  • 1
    refer to your recent Edit-reply source.subscribeWith(rs); source.subscribe(rs); both return SingleObserver instance, so you are right both can be used interchangeably i.e composite.add(one | another) . but Why someone choose to use one over the other remains unclear to me. sorry if I missed your point , but I do not see a specific use case, what I mean by that is i.e in this scenario we should use subscribeWith and subscribe does not deliver. TA – bastami82 Jul 14 '17 at 16:02
  • 1
    source.subscribe(rs); does not return an Observer. I've added further explanation under "edit 2" section of the answer. – mmaarouf Jul 14 '17 at 16:49
  • sorry but I think you are wrong because subscribe does return SingleObserver instance therefore it can be accepted as an parameter in compositeDisposable.add(); see my question for subscribe, I added it to compositeDisposable.add() in one line. I also added the documentation to the bottom of my question to explain they both return a SingleObserver instance. – bastami82 Jul 14 '17 at 20:26
  • 1
    No, you're confusing the subscribe methods. The subscribe method that i used in Edit 2 (& what's used in subscribeWith's implementation) takes in an OBSERVER object as it's single parameter and is a void method. The subscribe method you are mentioning accepts an onSuccess CONSUMER object, and an onError CONSUMER object as parameters, and returns a Disposable. Notice the different parameters and return types? These are 2 different methods. Please re-read the Observable#subscribe explanation section of my answer as it details the different subscribe methods. – mmaarouf Jul 15 '17 at 02:08
  • I read your comments carefully and thanks for steering this question back to the right direction ;) when to use which? and what is the use case for each: subscrib(new Consumer, new Consumer) and SubscribeWith(new Observer) , I am specifically concern about this Subscribe method. (I think you touched the answer with consumer/observer difference) but not sure when to favour one over the other yet. – bastami82 Jul 15 '17 at 05:31
  • So what's the downside of using the overloaded .subscribe() from Bastami1982's first code snippet? With that one we can use lambdas and still be able to add the Disposable to a CompositeDisposable in a single line. – Incinerator Oct 11 '17 at 12:46
  • 2
    It all depends on the use case. If you're going to subscribe and give a unique implementation of onNext, onError in one place then it would make sense to use the #subscribe that takes in lambdas. Otherwise, if you will need to reuse common Observer code in various different classes like the example i gave in edit3 or like with ResourceObservable example from the javadoc, then you can use the #subscribeWith. Use what best solves the problem :) – mmaarouf Oct 11 '17 at 13:13
  • @mmaarouf..Great effort Sir! Beautifully explained. – Rohan Dec 24 '17 at 21:22
  • Great answer! Thanks for writing this. Learned a lot! – Nari Kim Shin Sep 20 '18 at 17:00