WorkManager allows you to update a WorkRequest
after you have already
enenqueued it. This is often necessary in larger apps that frequently change
constraints or need to update their workers on the fly. As of WorkManager
version 2.8.0, the updateWork()
API is the means of doing this.
The updateWork()
method allows you to change certain aspects of a
WorkRequest
on the fly, without having to go through the process of manually
canceling and enqueuing a new one. This greatly simplifies the development
process.
Avoid canceling work
You should generally avoid canceling an existing WorkRequest and enqueuing a new one. Doing so can result in the app repeating certain tasks, and can require you to write a significant amount of additional code.
Consider the following examples of where canceling a WorkRequest can cause difficulties:
- Back-end request: If you cancel a
Worker
while it is computing a payload to send to the server, the newWorker
needs to start over and recompute the potentially expensive payload. - Scheduling: If you cancel a
PeriodicWorkRequest
and you would like the newPeriodicWorkRequest
to execute on the same schedule, you need to calculate a time offset to ensure that the new execution time is aligned with the previous work request.
The updateWork()
API allows you to update a work request's constraints and
other parameters without the trouble of canceling and enqueuing a new request.
When to cancel work
There are cases where you should directly cancel a WorkRequest
rather than
call updateWork()
. This is what you should do when you wish to change the
fundamental nature of the work that you have enqueued.
When to update work
Imagine a photo app that does a daily backup of the user's photos. It has
enqueued a PeriodicWorkRequest
to do so. The WorkRequest
has constraints
that require the device to be charging and connected to WiFi.
However, the user only charges their device for 20 minutes a day using a fast
charger. In this case, the app may want to update the WorkRequest
to relax the
charging constraint, so that it can still upload the photos even if the device
isn't fully charged.
In this situation, you can use the updateWork()
method to update the work
request's constraints.
How to update work
The updateWork()
method provides a simple means of updating an existing
WorkRequest
, without having to cancel and enqueue a new one.
To use update enqueued work follow these steps:
- Get the existing ID for enqueued work: Get the ID of the WorkRequest you
would like to update. You can retrieve this ID with any of the
getWorkInfo
APIs, or by manually persisting the ID from the initial WorkRequest for later retrieval with the public propertyWorkRequest.id
, before enqueuing it. - Create new WorkRequest: Create a new
WorkRequest
and useWorkRequest.Builder.setID()
to set its ID to match that of the existingWorkRequest
. - Set constraints: Use
WorkRequest.Builder.setConstraints()
to pass the WorkManager new constraints. - Call updateWork: Pass the new WorkRequest to
updateWork()
.
Update work example
Here is an example code snippet in Kotlin that demonstrates how to use the
updateWork()
method to change the battery constraints of a WorkRequest
used
to upload photos:
suspend fun updatePhotoUploadWork() {
// Get instance of WorkManager.
val workManager = WorkManager.getInstance(context)
// Retrieve the work request ID. In this example, the work being updated is unique
// work so we can retrieve the ID using the unique work name.
val photoUploadWorkInfoList = workManager.getWorkInfosForUniqueWork(
PHOTO_UPLOAD_WORK_NAME
).await()
val existingWorkRequestId = photoUploadWorkInfoList.firstOrNull()?.id ?: return
// Update the constraints of the WorkRequest to not require a charging device.
val newConstraints = Constraints.Builder()
// Add other constraints as required here.
.setRequiresCharging(false)
.build()
// Create new WorkRequest from existing Worker, new constraints, and the id of the old WorkRequest.
val updatedWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWorker>()
.setConstraints(newConstraints)
.setId(existingWorkRequestId)
.build()
// Pass the new WorkRequest to updateWork().
workManager.updateWork(updatedWorkRequest)
}
Handle the result
updateWork()
returns a ListenableFuture<UpdateResult>
. The given
UpdateResult
can have one of the several values that outline whether or not
WorkManager was able to apply your changes. It also indicates when it was able
to apply the change.
For more information, see the updateWork()
and UpdateResult
reference.
Track work with generations
Each time you update a WorkRequest
, its generation increments by one. This
lets you track exactly which WorkRequest
is currently enqueued.
Generations provide you more control when observing, tracing, and testing work
requests.
To get the generation of a WorkRequest
, follow these steps:
- WorkInfo: Call
WorkManager.getWorkInfoById()
to retrieve an instance ofWorkInfo
corresponding to yourWorkRequest
.- You can call one of several methods that return a
WorkInfo
. For more information, see the WorkManager reference.
- You can call one of several methods that return a
- getGeneration: Call
getGeneration()
on the instance ofWorkInfo
. TheInt
returned corresponds to the generation of theWorkRequest
.- Note that there isn't a generation field or property, only the
WorkInfo.getGeneration()
method.
- Note that there isn't a generation field or property, only the
Track generation example
The following is an example implementation of the workflow described above for
retrieving the generation of a WorkRequest
.
// Get instance of WorkManager.
val workManager = WorkManager.getInstance(context)
// Retrieve WorkInfo instance.
val workInfo = workManager.getWorkInfoById(oldWorkRequestId)
// Call getGeneration to retrieve the generation.
val generation = workInfo.getGeneration()
Policies for updating work
Previously, the recommended solution to updating periodic work was to enqueue a
PeriodicWorkRequest
with the policy ExistingPeriodicWorkPolicy.REPLACE
.
If there was a pending PeriodicWorkRequest
with the same unique id
, the new
work request would cancel and delete it. This policy is now deprecated in
favor of the workflow using the ExistingPeriodicWorkPolicy.UPDATE
.
For example, when using enqueueUniquePeriodicWork
with a
PeriodicWorkRequest
, you can initialize the new PeriodicWorkRequest
with the
ExistingPeriodicWorkPolicy.UPDATE
policy. If there is a pending
PeriodicWorkRequest
with the same unique name, WorkManager updates it to the
new specification. Following this workflow, it is not necessary to use
updateWork()
.