With WorkManager, you can easily set up a task and hand it off to the system to run under the conditions you specify.
This overview covers the most basic WorkManager features. In this page, you'll learn how to set up a task, specify the conditions under which it should run, and hand it off to the system. You'll also learn how to set up recurring jobs.
For information about more advanced WorkManager features, like job chaining and pass and return values, see WorkManager advanced features. There are many more features available; for full details, see the WorkManager reference documentation.
Classes and concepts
The WorkManager API uses several different classes. In some cases, you'll need to subclass one of the API classes.
These are the most important WorkManager classes:
Worker
: specifies what task you need to perform. The WorkManager APIs include an abstractWorker
class. You extend this class and perform the work here.WorkRequest
: represents an individual task. At a minimum, aWorkRequest
object specifies whichWorker
class should perform the task. However, you can also add details to theWorkRequest
object, specifying things like the circumstances under which the task should run. EveryWorkRequest
has an autogenerated unique ID; you can use the ID to do things like cancel a queued task or get the task's state.WorkRequest
is an abstract class; in your code, you'll be using one of the direct subclasses,OneTimeWorkRequest
orPeriodicWorkRequest
.WorkRequest.Builder
: a helper class for creatingWorkRequest
objects. Again, you'd be using one of the subclasses,OneTimeWorkRequest.Builder
orPeriodicWorkRequest.Builder
.Constraints
: specifies restrictions on when the task should run (for example, "only when connected to the network"). You create theConstraints
object withConstraints.Builder
, and pass theConstraints
to theWorkRequest.Builder
before creating theWorkRequest
.
WorkManager
: enqueues and manages the work requests. You pass yourWorkRequest
object toWorkManager
to enqueue the task.WorkManager
schedules the task in such a way as to spread out the load on system resources, while honoring the constraints you specify.WorkInfo
: contains information about a particular task. TheWorkManager
provides aLiveData
for eachWorkRequest
object. TheLiveData
holds aWorkInfo
object; by observing thatLiveData
, you can determine the current status of the task, and get any returned values after the task finishes.
Typical workflow
Suppose that you're writing a photo library app, and that app needs to periodically compress its stored images. You want to use the WorkManager APIs to schedule the image compression. In this case, you don't particularly care when the compression happens; you want to set up the task and forget about it.
First, you would define your Worker
class, and override its
doWork()
method. Your worker
class specifies how to perform the operation, but doesn't have any information
about when the task should run.
Kotlin
class CompressWorker(context : Context, params : WorkerParameters) : Worker(context, params) { override fun doWork(): Result { // Do the work here--in this case, compress the stored images. // In this example no parameters are passed; the task is // assumed to be "compress the whole library." myCompress() // Indicate success or failure with your return value: return Result.success() // (Returning Result.retry() tells WorkManager to try this task again // later; Result.failure() says not to try again.) } }
Java
public class CompressWorker extends Worker { public CompressWorker( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @Override public Result doWork() { // Do the work here--in this case, compress the stored images. // In this example no parameters are passed; the task is // assumed to be "compress the whole library." myCompress(); // Indicate success or failure with your return value: return Result.success(); // (Returning Result.retry() tells WorkManager to try this task again // later; Result.failure() says not to try again.) } }
Next, you create a OneTimeWorkRequest
object based on
that Worker
, then enqueue the task with
WorkManager
:
Kotlin
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>().build() WorkManager.getInstance().enqueue(compressionWork)
Java
OneTimeWorkRequest compressionWork = new OneTimeWorkRequest.Builder(CompressWorker.class) .build(); WorkManager.getInstance().enqueue(compressionWork);
WorkManager
chooses an appropriate time to run the task,
balancing such considerations as the load on the system, whether the device is
plugged in, and so on. In most cases, if you don't specify any constraints,
WorkManager
runs your task right away. If you need to check on
the task status, you can get a WorkInfo
object by getting a
handle to the appropriate LiveData<WorkInfo>
. For
example, if you want to check if the task has finished, you could use code like
this:
Kotlin
WorkManager.getInstance().getWorkInfoByIdLiveData(compressionWork.id) .observe(lifecycleOwner, Observer { workInfo -> // Do something with the status if (workInfo != null && workInfo.state.isFinished) { // ... } })
Java
WorkManager.getInstance().getWorkInfoByIdLiveData(compressionWork.getId()) .observe(lifecycleOwner, workInfo -> { // Do something with the status if (workInfo != null && workInfo.getState().isFinished()) { // ... } });
For more information about working with LiveData
, see the
LiveData overview.
Task constraints
If you wish, you can specify constraints on when the task should run. For
example, you might want to specify that the task should only run when the device
is idle, and connected to power. In this case, you'd need to create a
OneTimeWorkRequest.Builder
object, and use that
builder to create the actual OneTimeWorkRequest
:
Kotlin
// Create a Constraints object that defines when the task should run val myConstraints = Constraints.Builder() .setRequiresDeviceIdle(true) .setRequiresCharging(true) // Many other constraints are available, see the // Constraints.Builder reference .build() // ...then create a OneTimeWorkRequest that uses those constraints val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>() .setConstraints(myConstraints) .build()
Java
// Create a Constraints object that defines when the task should run Constraints myConstraints = new Constraints.Builder() .setRequiresDeviceIdle(true) .setRequiresCharging(true) // Many other constraints are available, see the // Constraints.Builder reference .build(); // ...then create a OneTimeWorkRequest that uses those constraints OneTimeWorkRequest compressionWork = new OneTimeWorkRequest.Builder(CompressWorker.class) .setConstraints(myConstraints) .build();
Then pass the new OneTimeWorkRequest
object to
WorkManager.enqueue()
, as
before. WorkManager
considers your constraints when finding a
time to run the task.
Canceling a Task
You can cancel a task after you enqueue it. To cancel the task, you need
its work ID, which you can get from the WorkRequest
object. For
example, the following code cancels the compressionWork
request from the
previous section:
Kotlin
val compressionWorkId:UUID = compressionWork.getId() WorkManager.getInstance().cancelWorkById(compressionWorkId)
Java
UUID compressionWorkId = compressionWork.getId(); WorkManager.getInstance().cancelWorkById(compressionWorkId);
WorkManager
makes its best effort to cancel the task, but this
is inherently uncertain--the task may already be running or finished when you attempt to
cancel it.
WorkManager
also provides methods to cancel all tasks in a
unique work sequence,
or all tasks with a specified tag,
also on a best-effort basis.
Tagged work
You can group your tasks logically by assigning a tag string to any
WorkRequest
object. To set a tag, call
WorkRequest.Builder.addTag(String)
,
for example:
Kotlin
val cacheCleanupTask = OneTimeWorkRequestBuilder<MyCacheCleanupWorker>() .setConstraints(myConstraints) .addTag("cleanup") .build()
Java
OneTimeWorkRequest cacheCleanupTask = new OneTimeWorkRequest.Builder(MyCacheCleanupWorker.class) .setConstraints(myConstraints) .addTag("cleanup") .build();
The WorkManager
classes provide several utility methods that
let you operate on all tasks with a particular tag. For example,
WorkManager.cancelAllWorkByTag(String)
cancels all tasks with a particular tag, and
WorkManager.getWorkInfosByTagLiveData(String)
returns a list of all the WorkInfo
for all tasks
with that tag.
Recurring tasks
You might have a task that you need to perform repeatedly. For example, the photo manager app probably wouldn't compress its photos just once. More likely, it would examine its shared photos every so often, and see if there are any new or changed images that need to be compressed. This recurring task could compress the images it finds, or alternatively, it could fire off new "compress this image" tasks when it finds an image that needs compressing.
To create a recurring task, use the
PeriodicWorkRequest.Builder
class to create a
PeriodicWorkRequest
object, then enqueue the
PeriodicWorkRequest
the same way you would a
OneTimeWorkRequest
object. For example, suppose we
defined a PhotoCheckWorker
class to identify images that need to be
compressed. If you want to run the inventory task every 12 hours, you would create
a PeriodicWorkRequest
object like this:
Kotlin
val photoCheckBuilder = PeriodicWorkRequestBuilder<PhotoCheckWorker>(12, TimeUnit.HOURS) // ...if you want, you can apply constraints to the builder here... // Create the actual work object: val photoCheckWork = photoCheckBuilder.build() // Then enqueue the recurring task: WorkManager.getInstance().enqueue(photoCheckWork)
Java
PeriodicWorkRequest.Builder photoCheckBuilder = new PeriodicWorkRequest.Builder(PhotoCheckWorker.class, 12, TimeUnit.HOURS); // ...if you want, you can apply constraints to the builder here... // Create the actual work object: PeriodicWorkRequest photoCheckWork = photoCheckBuilder.build(); // Then enqueue the recurring task: WorkManager.getInstance().enqueue(photoCheckWork);
The WorkManager
attempts to run your task at the interval you
request, subject to the constraints you impose and its other requirements.