24

I want to create an Observable from view click listener using RxJava 2. I started from the simplest implementation (I don't use lambdas here to show you different types in this method):

 Observable<View> viewObservable = Observable.create(new ObservableOnSubscribe<View>() {
        @Override
        public void subscribe(@NonNull ObservableEmitter<View> e) throws Exception {
            mNewWordView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View value) {
                    if (!e.isDisposed()) {
                        e.onNext(value);
                    }
                }
            });
        }
    });

Then I thought about the way to set onClickListener to null if it is not needed further. I found that there are two methods with similar (as for me) names:

e.setCancellable(Cancellable c); and e.setDisposable(Disposable d);

What is the difference between them and which should I use?

Gaket
  • 6,533
  • 2
  • 37
  • 67

2 Answers2

27

From the Javadoc:

[Cancellable is] A functional interface that has a single cancel method that can throw.

The Disposable is not a functional interface plus when implementing its dispose() method, you are not allowed to throw checked exceptions.

In contrast, many non-RxJava components return a Closeable or AutoCloseable which are defined via throws IOException and throws Exception and are somewhat of a burden because you'd need try-catch it.

For example, you'd want to use setCancellable when you work with a file:

Observable.create((ObservableEmitter<byte[]> e) -> {
    FileInputStream fin = new FileInputStream("raw.dat");
    e.setCancellable(fin::close);

    byte[] buffer = new byte[4096];

    for (;;) {
        int r = fin.read(buffer);
        if (r < 0) {
            break;
        }
        e.onNext(buffer);
    }
    e.onComplete();
});

and you'd use setDisposable if you use a Scheduler:

Observable.create((ObservableEmitter<Event> e) -> {
    Worker worker = Schedulers.io().createWorker();
    e.setDisposable(worker);

    eventSource.onEvent(es ->
        worker.schedule(() -> e.onNext(es))
    );
});
akarnokd
  • 69,132
  • 14
  • 157
  • 192
  • 1
    Thank you. Should I use `check if (!e.isDisposed())` if I use Cancellable and not Disposed? – Gaket Apr 08 '17 at 07:24
  • Yes, the downstream dispose call is properly reflected via isDisposed() no matter what resources you have. – akarnokd Apr 08 '17 at 08:33
  • 1
    For this basic case, you don't really need it but in case you want to emit an `onError`, it is advised to an isDisposed() check because if the emitter is disposed, the error will be routed to the global error handler which crashes an Android app often unexpectedly. – akarnokd Apr 08 '17 at 08:35
  • Nowadays it's safer to use `tryOnError` rather than `onError` + `isDisposed` – Luke Needham Apr 28 '21 at 17:15
21

The Cancellable ends up getting wrapped into a Disposable so the final effect is the same for either call. The difference is that Disposable has more features that you might not want to implement, so the simple Cancellable interface is there as an alternative.

If you just want to dispose something when the observable ends use Cancellable. If you have a resource that might be disposed because of some external reason you want to implement Disposable to implement the Disposable.isDisposed() method.

Do note that the methods are mutually exclusive. Only a single disposable or cancellable can be registered at once. Calling both overwrites the first one.

Kiskae
  • 24,655
  • 2
  • 77
  • 74
  • 1
    Thank you. Should I use check `if (!e.isDisposed())` if I use Cancellable and not Disposed? – Gaket Apr 07 '17 at 18:33
  • akarnokd is one of the RxJava developers and his example does not use it so that check if probably not required. As far as I know such a check is only required on `onError` calls because an error on a disposed observable will go to the global exception handler. – Kiskae Apr 07 '17 at 18:58
  • ~"**some external reason**". What kind of reason for this? – IgorGanapolsky Apr 09 '18 at 14:23
  • 2
    @IgorGanapolsky For example if you're listening for events from another system and that system broadcasts that it has ended, you can report that it has already been disposed, instead of potentially wasting time unregistering for events. – Kiskae Apr 09 '18 at 16:29