Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Defining your Work Requests

The getting started guide covered how to create a simple WorkRequest and enqueue it.

In this guide you will learn to customize work requests to handle common use cases:

  • Handle task constraints like network availabilty
  • Guarantee a minimum delay in task execution
  • Handle task retries and back-off
  • Handle task input & output
  • Group tasks with tagging

Work constraints

You can add Constraints to your work to indicate when it can run.

For example, you can specify that the work should only run when the device is idle and connected to power.

The code below shows how you can add these constraints to a OneTimeWorkRequest. For a full list of supported constraints, take a look at the Constraints.Builder reference documentation.

Kotlin


// Create a Constraints object that defines when the task should run
val constraints = Constraints.Builder()
        .setRequiresDeviceIdle(true)
        .setRequiresCharging(true)
        .build()

// ...then create a OneTimeWorkRequest that uses those constraints
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>()
        .setConstraints(constraints)
        .build()

Java

// Create a Constraints object that defines when the task should run
Constraints constraints = new Constraints.Builder()
    .setRequiresDeviceIdle(true)
    .setRequiresCharging(true)
     .build();

// ...then create a OneTimeWorkRequest that uses those constraints
OneTimeWorkRequest compressionWork =
                new OneTimeWorkRequest.Builder(CompressWorker.class)
     .setConstraints(constraints)
     .build();

When multiple constraints are specified, your task will run only when all the constraints are met.

In the event that a constraint fails while your task is running, WorkManager will stop your worker. The task will then be retried when the constraint(s) are met.

Initial Delays

In the event that your work has no constraints or that all the constraints are met when your work is enqueued, the system may choose to run the task immediately. If you do not want the task to be run immediately, you can specify your work to start after a minimum initial delay.

Here is an example of how to set your task to run atleast 10 minutes after it has been enqueued.

Kotlin


val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
        .setInitialDelay(10, TimeUnit.MINUTES)
        .build()


Java


OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
        .setInitialDelay(10, TimeUnit.MINUTES)
        .build();

Retries and Backoff Policy

If you require that WorkManager retry your task, you can return Result.retry() from your worker.

Your work is then rescheduled with a default backoff delay and policy. The backoff delay specifies the minimum amount of time to wait before retrying the work. The backoff policy defines how the backoff delay is going to increase over time for the following retry attempts; it is EXPONENTIAL by default.

Here is an example of customizing the backoff delay and policy.

Kotlin


val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
        .setBackoffCriteria(
                BackoffPolicy.LINEAR,
                OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                TimeUnit.MILLISECONDS)
        .build()


Java


OneTimeWorkRequest uploadWorkRequest = 
    new OneTimeWorkRequest.Builder(UploadWorker.class)
        .setBackoffCriteria(
                BackoffPolicy.LINEAR,
                OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                TimeUnit.MILLISECONDS)
        .build();

Defining input/output for your task

Your task may require data to be passed in as input parameters or be returned as a result. For example, a task that handles uploading an image requires the URI of the image to be uploaded as input and may require the URL of the uploaded image as the output.

Input and output values are stored as key-value pairs in a Data object. The code below shows how you can set input data in your WorkRequest.

Kotlin


// workDataOf (part of KTX) converts a list of pairs to a [Data] object.
val imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)

val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
        .setInputData(imageData)
        .build()

Java


Data imageData = new Data.Builder
                .putString(Constants.KEY_IMAGE_URI, imageUriString)
                .build();

OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
        .setInputData(imageData)
        .build()

The Worker class can access the input arguments by calling Worker.getInputData().

Similarily, the Data class can be used to output a return value. Return the Data object by including it in the Result on Result.success() or Result.failure(), as shown below.

Kotlin

class UploadWorker(appContext: Context, workerParams: WorkerParameters)
    : Worker(appContext, workerParams) {

    override fun doWork(): Result {

            // Get the input
            val imageUriInput = getInputData().getString(Constants.KEY_IMAGE_URI)
            // TODO: validate inputs.
            // Do the work
            val response = uploadFile(imageUriInput)

            // Create the output of the work
            val outputData = workDataOf(Constants.KEY_IMAGE_URL to response.imageUrl)

            // Return the output
            return Result.success(outputData)

    }
}


Java

public class UploadWorker extends Worker {

    public UploadWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Result doWork() {

        // Get the input
        String imageUriInput = getInputData().getString(Constants.KEY_IMAGE_URI)
        // TODO: validate inputs.
        // Do the work
        Response response = uploadFile(imageUriInput)

        // Create the output of the work
        Data outputData = new Data.Builder
                .putString(Constants.KEY_IMAGE_URL, response.imageUrl)
                .build();

        // Return the output
        return Result.success(outputData)
    }
}

Tagging work

You can group your tasks logically by assigning a tag string to any WorkRequest object. This allows you to 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 LiveData with a list of the status of all tasks with that tag.

The following code shows how you can add a "cleanup" tag to your task with WorkRequest.Builder.addTag(String):

Kotlin

val cacheCleanupTask =
        OneTimeWorkRequestBuilder<CacheCleanupWorker>()
    .setConstraints(constraints)
    .addTag("cleanup")
    .build()

Java

OneTimeWorkRequest cacheCleanupTask =
        new OneTimeWorkRequest.Builder(CacheCleanupWorker.class)
    .setConstraints(constraints)
    .addTag("cleanup")
    .build();