Waiting for a Worker to finish

1.3k views Asked by At

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?

2

There are 2 answers

0
SatNav On

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()
        }
    }
}
3
Sean Blahovici On

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.