1

I have a moderately long-running (a minute or two) background process, which I'm running as a Worker using WorkManager. There are two places from where it can be started - either from the UI, or from another background process.

In the case of the UI, of course I can observe it as LiveData, and update when it changes.

However in the case of the background process, I'd like to be able to enqueue the Worker, and then wait for it to complete, blocking until it's finished.

I've tried using runBlocking, for example:

val request = OneTimeWorkRequestBuilder<LibraryCacheWorker>()
        .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
        .build()
runBlocking { WorkManager.getInstance(context).enqueue(request).result.await() }

But it just returns straight away while the process continues on another thread. How can I wait for the actual work to finish before continuing?

SatNav
  • 33
  • 6
  • you can observe the work without blocking just use `getWorkInfoByXXLiveData` and register for changes ... you can fx grayout UI/disable UI/show indeterminate progress and then unlock when work is done – Selvin Apr 20 '22 at 14:15
  • Thank you, but I *want* to block. – SatNav Apr 20 '22 at 14:17
  • No, you dont want do this ... It will cause ANR – Selvin Apr 20 '22 at 14:17
  • How will it? It's taking place in the context of a background thread. – SatNav Apr 20 '22 at 14:18
  • "However in the case of the background process" -- then why are you using `WorkManager`? You are already in the background. – CommonsWare Apr 20 '22 at 14:25
  • @CommonsWare I want a way so that when I open the UI, if the process is already running (ie. it was started from the background), the UI can discover it, and indicate it with a progress wheel on the screen. Is there another method that might be better suited to what I'm trying to do? – SatNav Apr 20 '22 at 14:28
  • OK, then, could you get rid of the background process and have that be part of a chained set of work? – CommonsWare Apr 20 '22 at 14:43
  • Unfortunately not. The background process is actually a DocumentsProvider which I'm writing as a plugin for another app. – SatNav Apr 20 '22 at 15:05

2 Answers2

1

Have your worker write some sort of flag with relevant info into room, datastore or other persistence, observe that data where you need it, and react when the data is persisted.

The way you are trying to do it goes against the nature of WorkManager, which runs the work when it thinks is the best time.

Edit: If you really want to block, then don't use WorkManager, IMO. Just call the code synchronously.

Sean Blahovici
  • 5,350
  • 4
  • 28
  • 38
  • Thank you. Interesting thought, I may try it. Is there no 'waitfor' function on Workers that you know of? Is there another method that may be more suitable for what I'm trying to do? I want a background process that can be either 'observed' on the UI thread, or else 'waited for' from another background thread. – SatNav Apr 20 '22 at 14:23
  • I started by calling the code synchronously, and it works fine - however I want a way that when I open the UI, if the process is already running, the UI can discover it, and indicate it with a progress wheel on the screen. – SatNav Apr 20 '22 at 14:26
  • You can write a flag to persistence when the job starts, and when it ends. Then in the UI, check for that flag and show loading if it is ongoing, remove loading when done! I am not aware of a wait function for workers. – Sean Blahovici Apr 20 '22 at 14:33
0

I resolved this using a CountDownLatch in the end.

The background process calls the function synchronously, as per CommonsWare's suggestion, and the UI starts a Worker, which calls the function. However the function itself uses a CountDownLatch to detect whether it is already running in another thread.

Like this:

private var latch = CountDownLatch(0)

fun rebuildCache() {
    if (latch.count > 0)
        latch.await()
    else {
        latch = CountDownLatch(1)
        try {
            //
            // do the work...
            //
        } finally {
            latch.countDown()
        }
    }
}
SatNav
  • 33
  • 6