Home » Android » java – What thread is unSubscribeOn called? Should we call it?

java – What thread is unSubscribeOn called? Should we call it?

Posted by: admin June 15, 2020 Leave a comment

Questions:

What thread is unsubscribeOn defaulted to when we do not specify it but still specify the thread for subscribeOn?
Are we required to specify the thread that we want un-subscription to happen on even when it is the same thread as the one used in subscribeOn?

Or are the bottom two snippets doing the same for un-subscription?

Option 1:

mSubscription.add(myAppProvider
        .getSomeData()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .unsubscribeOn(Schedulers.io())
        .subscribe(data -> handleData(data),
                throwable -> handleError(throwable)
                ));

Option 2:

mSubscription.add(myAppProvider
        .getSomeData()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(data -> handleData(data),
                throwable -> handleError(throwable)
                ));

I did look at the Rx-Java docs but they only explain subscribeOn but nothing about unSubscribeOn

How to&Answers:

Those snippets have different behaviours.

In general, unsubscription travels up the operator sequence and can be initiated by any thread at all (just call subscriber.unsubscribe() from any thread). Without the presence of an unsubscribeOn operator the unsubscribe action will probably complete its action on the thread it was called from. unsubscribeOn provides finer control of the thread used for unsubscription upstream of it.

Answer:

Without subscribeOn (and without observeOn), your unsubscription actions will occur on whatever thread the subscription started.

With subscribeOn, your unsubscription actions will occur on the Scheduler specified by subscribeOn.

With observeOn, your unsubscription actions will occur on the Scheduler specified by observeOn (overriding the Scheduler specified by subscribeOn).

Here is a sample. As suggested there, this is useful when the unsubscription itself involves long-running operations that you want to run on some other thread.

If you run their test code:

Observable<Object> source = Observable.using(
    () -> {
        System.out.println("Subscribed on " + Thread.currentThread().getId());
        return Arrays.asList(1,2);
    },
    (ints) -> {
        System.out.println("Producing on " + Thread.currentThread().getId());
        return Observable.from(ints);
    },
    (ints) -> {
        System.out.println("Unubscribed on " + Thread.currentThread().getId());
    }
);

source
    .unsubscribeOn(Schedulers.newThread())
    .subscribe(System.out::println);

You should see their expected output:

 Subscribed on 1
 Producing on 1
 1
 2
 Unubscribed on 11

If you remove that unsubscribeOn line, you’ll see:

 Unsubscribed on 1

Answer:

What thread is unsubscribeOn defaulted to when we do not specify it
but still specify the thread for subscribeOn?

  • By default when neither of subscribeOn/observeOn/unsubscribeOn are
    set, then unsubscribeOn (as well as others) are defaulted to the
    current thread.

  • If we do set a thread for subscribeOn and none for
    observeOn/unsubscribeOn, then unsubscribeOn will use the same thread
    specified in subscribeOn.

  • If we call both subscribeOn and ObserveOn but not unsubscribeOn, then
    unsubscribeOn will use the thread specified in observeOn.

  • If all three methods (subscribeOn, observeOn and unsubscribeOn) are
    set, then unsubscribeOn will use the thread specified in
    unsubscribeOn. In fact unsubscribeOn will happen on the thread
    specified in unsubscribeOn method regardless of weather the previous
    methods are set or not.

Are we required to specify the thread that we want un-subscription to
happen on even when it is the same thread as the one used in
subscribeOn?

  • As explained above if unsubscribeOn is not set then unsubscribeOn
    happens on observeOn if it is set. If not then it happens on the
    thread set by subscribeOn. Now we do not need to set a different
    thread for unsubscribeOn unless you are doing some long running task
    while unsubscribing. In most cases or atleast from my code, this is
    true and so we do not have to set a different thread.

Here is a sample that I created that can be used to test the above in Android. Just comment out or change the threads as required to test various outcomes.

    public void testRxThreads() {
        createThreadObservable()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .unsubscribeOn(Schedulers.newThread())
            .subscribe(printResult());
    }

    private Observable<String> createThreadObservable() {
        return Observable.create(subscriber -> {
            subscriber.add(new Subscription() {
                @Override
                public void unsubscribe() {
                    System.out.println("UnSubscribe on Thread: "
                        + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
                    // perform unsubscription
                }

                @Override
                public boolean isUnsubscribed() {
                    return false;
                }
            });

            subscriber.setProducer(n -> {
                System.out.println("Producer thread: "
                    + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
            });

            subscriber.onNext("Item 1");
            subscriber.onNext("Item 2");
            subscriber.onCompleted();
        });
    }

    private Action1<String> printResult() {
        return result -> {
            System.out.println("Subscriber thread: "
                + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
            System.out.println("Result: " + result);
        };
    }

This produced the following result

Producer thread: 556 RxIoScheduler-2
Subscriber thread: 1 main
Result: Item 1
Subscriber thread: 1 main
Result: Item 2
UnSubscribe on Thread: 557 RxNewThreadScheduler-1

Commenting out or removing unsubscribeOn produces the following.

Subscriber thread: 1 main
Result: Item 1
Subscriber thread: 1 main
Result: Item 2
UnSubscribe on Thread: 1 main

Removing out both observeOn and unsubscribeOn calls produce the following:

Producer thread: 563 RxIoScheduler-2
Subscriber thread: 563 RxIoScheduler-2
Result: Item 1
Subscriber thread: 563 RxIoScheduler-2
Result: Item 2
UnSubscribe on Thread: 563 RxIoScheduler-2

Thanks to drhr for the initial explanation of events. That helped me research more and verify the outcomes with the sample above.