WorkManager provides a set of APIs that makes it easy to schedule asynchronous tasks for immediate or deferred execution that are expected to run even if the app is closed or the device restarts. For Kotlin users, WorkManager provides first-class support for coroutines. In this post I’ll show you the basics of WorkManager with coroutines by building on the WorkManager codelab. So let’s get started!
The basics of WorkManager
The WorkManager library is the recommended choice for any task that should continue to run, even if the user navigates away from the particular screen, the user puts the application in background or the device restarts. Common tasks could be:
- Uploading logs or reporting data
- Applying filters to images and saving the image
- Periodically syncing local data with the network
If your immediate task can end when the user leaves a certain scope such as a screen, we recommend you use Kotlin Coroutines directly.
The WorkManager codelab blurs images and saves the result on disk. Let’s see what was needed to achieve this.
We added the
We started by implementing our own Worker class. This is where we put the code for the actual work you want to perform in the background. You’ll extend the Worker class and override the doWork() method. As this is the most important class we’ll go over it in detail later. Here’s what the initial implementation looks like.
Then, we build our work request, in our case, we want to perform the work only once so we use a
OneTimeWorkRequest.Builder. As input, we set the
Uri of the image we want to blur.
Kotlin tip: to create the input data, we can use the
workDataOf function that creates the data builder, puts the key-value pair and creates the data for us.
To schedule the work and make it run, we use the WorkManager class. We can provide tasks to be done and constraints to these tasks.
Make the Worker do the work
When you use a
Worker, WorkManager automatically calls
Worker.doWork() on a background thread. The
Result returned from
doWork() informs the WorkManager service whether the work succeeded and, in the case of failure, whether or not the work should be retried.
Worker.doWork() is a synchronous call — you are expected to do the entirety of your background work in a blocking fashion and finish it by the time the method exits. If you call an asynchronous API in doWork() and return a Result, your callback may not operate properly.
But what if we want to do asynchronous work?
Let’s complicate our example and say that we want to save the Uris of all the files that have been blurred in the database.
For this I created:
- A simple BlurredImage entity
- A dao class to insert and get images
- The database
Check out the implementation here.
If you have to do asynchronous work, like saving data in the database or doing network requests, in Kotlin, we recommend using a
A CoroutineWorker allows us to do asynchronous work, using Kotlin coroutines.
doWork() method is a
suspend method. So this means that we can easily call our suspending dao here.
Dispatchers.Default. You can override this with the Dispatcher that you need. In our case, we don’t need to do this because Room already moves the insert work on a different Dispatcher. Check out the Room Kotlin APIs post for more details.
CoroutineWorker to perform asynchronous work that needs to complete even if the user closes your app.
If you want to learn more about WorkManager, stay tuned for a future series dedicated to covering it in depth. Until then, check out our codelabs and documentation: