23

I am using RxAndroid for stream operations. In my real use-case, i am fetching a list from the server (using Retrofit). I am using schedulers to do the work on a background thread and get the final emission on the Android UI (main) thread.

This works well for the network call, however i realized that my operators after the network call do not use the background thread, but getting called on the main thread.

myService.fetchSomeIntegersFromServer()
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .filter(integer -> {
            System.out.println(Looper.getMainLooper().getThread() == Thread.currentThread());
            return true;
        })
        .subscribe(integer1 -> {});

How can i make sure that all operations are executed on a background thread?

WonderCsabo
  • 11,947
  • 13
  • 63
  • 105

1 Answers1

63

TL;DR: move observeOn(AndroidSchedulers.mainThread()) below filter(...).


subscribeOn(...) is used to designate on which thread the Observable will begin operating on. Subsequent calls to subscribeOn will be ignored.

Thus, if you were to write the following, everything would be executed on Schedulers.newThread():

myService.fetchSomeIntegersFromServer()
        .subscribeOn(Schedulers.newThread())
        .filter(integer -> {
            System.out.println(Looper.getMainLooper().getThread() == Thread.currentThread());
            return true;
        })
        .subscribe(integer1 -> { doSomething(integer1); });

Now, of course, this is not what you want: you want to doSomething on the main thread.
That is where observeOn comes into place. All actions after observeOn are executed on that scheduler. Therefore, in your example, the filter is executed on the main thread.

Instead, move observeOn down to just before subscribe:

myService.fetchSomeIntegersFromServer()
        .subscribeOn(Schedulers.newThread())
        .filter(integer -> {
            System.out.println(Looper.getMainLooper().getThread() == Thread.currentThread());
            return true;
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(integer1 -> { doSomething(integer1) });

Now, filter will happen on the 'new thread', and doSomething on the main thread.


To go even further, you can use observeOn multiple times:

myService.fetchSomeIntegersFromServer()
        .subscribeOn(Schedulers.newThread())
        .observeOn(Schedulers.computation())
        .filter(integer -> {
            System.out.println(Looper.getMainLooper().getThread() == Thread.currentThread());
            return true;
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(integer1 -> { doSomething(integer1) });

In this case, the fetching will occur on a new thread, the filtering on a computation thread, and doSomething on the main thread.

Checkout ReactiveX - SubscribeOn operator for the official documentation.

nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • Thanks for the detailed answer! Is there a way to define the schedulers at the start of the method call chain (like in the OP)? – WonderCsabo Oct 27 '15 at 21:27
  • The Observables follow the [Decorator Pattern](https://en.wikipedia.org/wiki/Decorator_pattern), so by design, that is not possible. `observeOn` (and the other methods) do not return the same `Observable` instance like the builder pattern does, but instead return a _new_ `Observable` 'wrapping' the 'old' `Observable`. – nhaarman Oct 27 '15 at 21:33
  • I understand that. This is unfortunate for the case, because that means i have to add the `observeOn()` call to every stream i write? :( – WonderCsabo Oct 27 '15 at 21:36
  • What do you mean by that? – nhaarman Oct 27 '15 at 21:37
  • If i want to fetch and filter another list from the server, i also have to add the `.observeOn(AndroidSchedulers.mainThread())` line. (Currently i am caching the observable, and i thought that the first `observeOn()` call will take care.) – WonderCsabo Oct 27 '15 at 21:39
  • Ah, I see. If caching the `Observable` returned by Retrofit that way is allowed, then you should be able to cache the entire chain up to (not including) `subscribe`? Subsequent calls could use that cached `Observable` again, already containing `observeOn(mainThread())`. – nhaarman Oct 27 '15 at 21:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93544/discussion-between-wondercsabo-and-nhaarman). – WonderCsabo Oct 27 '15 at 21:49
  • 1
    Awesomely detailed and helpful answer. Thank you. – Mike Rapadas Jun 13 '16 at 01:47
  • 2
    great answer, 10x! – gor Sep 27 '17 at 15:38