2

I'm trying to figure out how to chain to observables together. I have an existing method: public static Observable<Data> getData(). In my other class I have this existing code:

doSomeBackgroundWork()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<..>() { ... })

I'd like to now chain the getData() call to this call. How would I do this? I tried This initially:

doSomeBackgroundWork()
.flatMap(s -> call() {
   mApi.getData()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<..>() { ... })

But this doesn't work, because the getData() code is actually executed on the main thread.

Even this doesn't work:

doSomeBackgroundWork()
.concatMap(s -> call() {
   mApi.getData()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<..>() { ... })

Also, when I try this the problem is that zipWith means the two observables run in parallel and I really want then to run one after the other.

doSomeBackgroundWork()
.zipWith(mApi.getData()),
    new Func2<BgWork, DataResponse,DataResponse>() {
    @Override
    public DataResponse call(BgWork bgWork, DatResponse data) {
       return data;
    }})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<..>() { ... })
b.lyte
  • 6,518
  • 4
  • 40
  • 51

1 Answers1

3

flatMap operator is the way to go here, you just need to handle the concurrency. If you want to run the whole getData() method on io scheduler, than you can just apply observeOn operator before the flatMap and then again after it like this:

doSomeBackgroundWork()
  .observeOn(Schedulers.io())
  .flatMap(s -> call() {
       mApi.getData()
  }
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Subscriber<..>() { ... })

You see, subscribeOn operator forces the producer to "compute" and emit data on provided Scheduler, so it doesn't matter where do you use it in the stream composition and it also have no effect when used multiple times. But that is not the case with observeOn operator. It rather tells the next stream to perform work on another Scheduler. This means, that when you use it again later, you can again divert the computation to another Scheduler.

However, if you only need to perform the work that is produced by the observable returned from getData() method on another Scheduler, you can use subscribeOn on this observable rather on the main stream.

doSomeBackgroundWork()
  .flatMap(s -> call() {
       mApi.getData().subscribeOn(Schedulers.io())
  }
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Subscriber<..>() { ... })
koperko
  • 2,447
  • 13
  • 19
  • Thank You! This is what I was looking for. I'm going with the second method where I explicitly tell `getData()` to `subscribeOn(Schedulers.io())` however I'm curious how these are different.. I guess it's simply two ways of achieving the same thing (in this case anyway). I assumed that `.flatMap(..)` was considered part of the computation hence the `.subscribeOn(...)` call at the bottom of the chain would indicate where the `.flatMap(...)` would do it's work. Looking at the documentation on ReactiveX there is nothing documenting this behavior. Where can I read on this behavior? – b.lyte Jan 10 '17 at 21:34
  • Honestly, this kind of behavior is not really well documented, but it makes sense. `flatMap` only subscribes to some other observable on a thread you specify by `observeOn`, but it is still a self contained stream that should handle concurrency within itself. – koperko Jan 11 '17 at 05:42
  • And to the point about how those two approaches are different. The first one -- as I tried to describe in the answer -- is running whole `flatMap` callback on `io` Scheduler, which means that if `getData()` method contained for example some blocking operation that is required for the `Observable` creation ( i.e. some heavy computing to get arguments you need to send to server with the request ), computation will happen on this Scheduler as well, whereas in the second approach you are just telling the resulting `Observable` to do its work on it, not the computation within `getData()` method. – koperko Jan 11 '17 at 05:49
  • As I didn't know what kind of work are you doing in there, I felt the need to describe both approaches – koperko Jan 11 '17 at 05:51