Save the date! Android Dev Summit is coming to Mountain View, CA on November 7-8, 2018.

WorkManager basics

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 recurrring 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:

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 RETRY tells WorkManager to try this task again
        // later; 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 Worker.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 RETRY tells WorkManager to try this task again
        // later; 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 WorkStatus object by getting a handle to the appropriate LiveData<WorkStatus>. For example, if you want to check if the task has finished, you could use code like this:

Kotlin

WorkManager.getInstance().getStatusById(compressionWork.id)
                .observe(lifecycleOwner, Observer { workStatus ->
                    // Do something with the status
                    if (workStatus != null && workStatus.state.isFinished) {
                        // ...
                    }
                })

Java

WorkManager.getInstance().getStatusById(compressionWork.getId())
    .observe(lifecycleOwner, workStatus -> {
        // Do something with the status
        if (workStatus != null && workStatus.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(), 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() cancels all tasks with a particular tag, and WorkManager.getStatusesByTag() returns a list of all the WorkStatus 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 an 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

new 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.