24

I'm fairly new to RxJava so this is probably a dumb question. I am going to describe my scenario.

I have some code running on the UI thread which will update some images but those images are not very important and they consume a bit of resources while generating them so I want to generate them on a single thread (not the UI thread of course) and generate them one by one. I'm guessing the trampoline scheduler is what I want but my problem is that if I use it then it does the work on the UI thread and I want it to do it on another thread.

Obviously I can write my own thread in which I can queue items and then it processes those one by one but I thought maybe RxJava would have a simple solution for me?

My current code looks like this:

Observable<Bitmap> getImage = Observable.create(new Observable.OnSubscribe<Bitmap>() {
    @Override public void call(Subscriber<? super Bitmap> subscriber) {
        Log.w(TAG,"On ui thread? "+ UIUtils.isRunningOnUIThread());
        subscriber.onNext(doComplexTaskToGetImage());
        subscriber.onCompleted();
    }
});

getImage.subscribeOn(Schedulers.trampoline()).subscribe(new Action1<Bitmap>() {
    @Override public void call(Bitmap bitmap) {
        codeToSetTheBitmap(bitmap);
    }
});

My log that says "On ui thread?" always has true. So how do I make that code and all subsequent attempts to do the same thing run on a single thread (not the ui thread) in order without writing a bunch of code to queue that work?

Edit:

I believe that this can now be accomplished using Schedulers.single() or if you want your own you can use new SingleScheduler(). I'm still testing but I think it does what I wanted back when I posted this.

casolorz
  • 8,486
  • 19
  • 93
  • 200
  • http://reactivex.io/documentation/operators/subscribeon.html https://github.com/ReactiveX/RxAndroid – corsair992 Apr 17 '15 at 17:24
  • I'm already using RxAndroid and my code shows that I am using `subscribeOn`. I just don't know what to pass to `subscribeOn` to do what I want. – casolorz Apr 17 '15 at 18:57
  • 1
    You need to subscribe on a background thread `Scheduler` (the [`Schedulers` class](http://reactivex.io/RxJava/javadoc/rx/schedulers/Schedulers.html) contains static factory methods for creating them). Then you need to observe on a main thread `Scheduler`, which can be acquires in RxAndroid via the `AndroidSchedulers.mainThread()` method. – corsair992 Apr 17 '15 at 19:18
  • 1
    I get that but which `Scheduler` will give me a background thread that will keep on getting reused working just like the `trampoline` scheduler? – casolorz Apr 17 '15 at 19:37
  • 2
    You could create a `ThreadPoolExecuter` with a pool size of 1 (`Executors.newSingleThreadExecutor()`), then use it to generate the schedulers via the `Schedulers.from()` method. Alternatively, you could create a custom `Thread` with a `Looper` running (e.g. `HandlerThread`), and pass a `Handler` running on that `Looper` to the `AndroidSchedulers.handlerThread()` method to get a `Scheduler` that will operate on that thread. – corsair992 Apr 17 '15 at 20:55
  • Interesting. Thanks, I'll give those a try. – casolorz Apr 18 '15 at 01:35
  • `Executors.newSingleThreadExecutor()` seems to be doing exactly what I wanted. Thank you. If you want to put it into an answer I'll gladly mark it as answered. – casolorz Apr 18 '15 at 13:03

3 Answers3

18

You can create a single reusable thread to create a Scheduler for the Observable in one of the following ways:

  • Create a ThreadPoolExecuter with a pool size of 1 (Executors.newSingleThreadExecutor() is a convenient static factory method for doing that), then use it to generate the schedulers via the Schedulers.from() method.
  • RxAndroid provides a custom Scheduler implementation that uses a Handler to schedule the actions, and thus can be used with any Thread that has a Looper running by passing it's Handler to the AndroidSchedulers.handlerThread() factory method.

Note that you will need to observe on a main thread Scheduler if you're interacting with the UI at the conclusion of these tasks.

corsair992
  • 3,050
  • 22
  • 33
16

In RxJava 2 you can use Schedulers.single() which:

Returns a default, shared, single-thread-backed Scheduler instance for work requiring strongly-sequential execution on the same background thread.

Please see the documentation for more details.

I don't see it available in RxJava 1 Schedulers documentation.

Albert Vila Calvo
  • 15,298
  • 6
  • 62
  • 73
  • 2
    But isnt single() returning always the same scheduler? So if you have 2 different items using single() they will both share the same thread, would they? So this doesnt answer the question since the thread wouldnt be dedicated to a job – FrankMonza Oct 15 '18 at 08:58
  • @FrankMonza of course `single()` always returns the same thread since you want the work to be sequentially (that's the whole point of it). If you want two independent single-threaded schedulers then use [`Schedulers.from(Executor executor)`](http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/schedulers/Schedulers.html#from-java.util.concurrent.Executor-), as explained on the other answer. – Albert Vila Calvo Oct 22 '18 at 18:14
0

You are using trampoline scheduler, so it means your source observable will be run on the current thread (here is main thread).

And the subscribeOn will work for both upstream and downstream. That why your log shows your are running on the main thread

To fix this, you can use Schedulers.single() in the subscribeOn, and then observeOn the main thread.

Trieu Tran
  • 1
  • 1
  • 1