0

I have some expensive operations that only need to be performed once (e.g. load/ download large files, load large ML models, or calculate optimized data structure based on some other data). I want to use this for every value the Observable/ Flowable generates:

The following code works, but it runs heavyProcessing() and heavyProcessing2() on the caller's thread. In my case, I can't choose what my callers thread (its the main thread because I am using WorkManager's RxWorker, which calls createWork from main). Therefore, start blocks the main thread. How do I get heavyProcessing to be performed in the background with RxJava and also available to the subsequent RxJava chain?

fun start(): Observable<Unit> {
    val heavy = heavyProcessing() // the heavy value i want to use everywhere!
    val anotherHeavyObject = heavyProcessing2()
    val items = Observable.fromIterable(listOfHundredsOfItems)
             .map { doSomeWork(it, heavy) }
             .map { doSomeWork(it, anotherHeavyObject) }
}

My attempts has so far not worked:

  1. Create a wrapper around the existing function: The issue with this code is the Observable returned by start() does not get observed, so the doSomeWork doesn't actually get done. I only know this because I put breakpoints in at doSomeWork, and it never gets called.
    fun startInBackground(): Single<Unit> {
        return Single.fromCallable {
            start()
        }
    }
  1. I've been trying to find ways of 'unnesting' the inner Observable (inside the Single), as that's probably the issue here. The inner Observable is not being observed.

This RxJava stuff is very unintuitive even after reading the guide

Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167
  • Its might be related to "deferred-dependent" https://github.com/ReactiveX/RxJava#deferred-dependent though I have no idea why. The code doesn't make sense but I understand `implicit data dependency between the previous sequence and the new sequence `. – Ben Butterworth Feb 12 '21 at 18:58

1 Answers1

0

Yes, it was related to Deferred-dependent. The example in the docs state:

Sometimes, there is an implicit data dependency between the previous sequence and the new sequence that, for some reason, was not flowing through the "regular channels". One would be inclined to write such continuations as follows:

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.defer(() -> Single.just(count.get())))
  .subscribe(System.out::println);

Actually, all I needed the caller to do is:

Single.defer { start() }.map { doMoreWork() }

instead of

start().map { doMoreWork() }
Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167
  • I just realized, the answer to this question can also be found here https://stackoverflow.com/questions/31498847/androidrx-run-method-in-background – Ben Butterworth Feb 12 '21 at 19:51