WorkManager 允许您在已入队后更新 WorkRequest。这对于经常更改限制或需要动态更新其 Worker 的大型应用来说通常是必要的。从 WorkManager 2.8.0 版本开始,您可以使用 updateWork() API 执行此操作。
updateWork() 方法允许您动态更改 WorkRequest 的某些方面,而无需手动取消并入队新的请求。这极大地简化了开发流程。
避免取消工作
通常应避免取消现有 WorkRequest 并入队新的请求。这样做可能导致应用重复某些任务,并且可能需要您编写大量额外的代码。
请考虑以下取消 WorkRequest 可能导致困难的示例:
- 后端请求:如果您在
Worker计算要发送到服务器的负载时取消它,那么新的Worker需要重新开始并重新计算这个可能成本很高的负载。 - 调度:如果您取消了
PeriodicWorkRequest,并且希望新的PeriodicWorkRequest按照相同的计划执行,则需要计算一个时间偏移量,以确保新的执行时间与之前的工作请求保持一致。
updateWork() API 允许您更新工作请求的限制和其他参数,而无需取消并入队新的请求。
何时取消工作
在某些情况下,您应该直接取消 WorkRequest,而不是调用 updateWork()。当您希望更改已入队工作的基本性质时,就应该这样做。
何时更新工作
想象一个照片应用,它每天备份用户的照片。它已入队一个 PeriodicWorkRequest 来执行此操作。该 WorkRequest 具有要求设备充电并连接到 WiFi 的限制。
然而,用户每天只使用快速充电器给设备充电 20 分钟。在这种情况下,应用可能希望更新 WorkRequest 以放宽充电限制,以便即使设备未充满电也能上传照片。
在这种情况下,您可以使用 updateWork() 方法来更新工作请求的限制。
如何更新工作
updateWork() 方法提供了一种简单的方法来更新现有 WorkRequest,而无需取消并入队新的请求。
要更新已入队的工作,请按以下步骤操作:
- 获取已入队工作的现有 ID:获取您要更新的 WorkRequest 的 ID。您可以使用任何
getWorkInfoAPI 检索此 ID,或者在入队之前通过公共属性WorkRequest.id手动持久化初始 WorkRequest 的 ID 以便以后检索。 - 创建新的 WorkRequest:创建新的
WorkRequest,并使用WorkRequest.Builder.setID()将其 ID 设置为与现有WorkRequest的 ID 匹配。 - 设置限制:使用
WorkRequest.Builder.setConstraints()将新的限制传递给 WorkManager。 - 调用 updateWork:将新的 WorkRequest 传递给
updateWork()。
更新工作示例
以下是 Kotlin 中的一个代码段示例,演示了如何使用 updateWork() 方法更改用于上传照片的 WorkRequest 的电池限制:
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)
}
处理结果
updateWork() 返回一个 ListenableFuture<UpdateResult>。给定的 UpdateResult 可以具有多个值中的一个,这些值概述了 WorkManager 是否能够应用您的更改,以及何时能够应用这些更改。
如需了解更多信息,请参阅 updateWork() 和 UpdateResult 参考文档。
使用代次跟踪工作
每次更新 WorkRequest 时,其“代次”都会递增一。这让您可以准确地跟踪当前入队的是哪个 WorkRequest。在观察、跟踪和测试工作请求时,代次可为您提供更多控制。
要获取 WorkRequest 的代次,请按以下步骤操作:
- WorkInfo:调用
WorkManager.getWorkInfoById()以检索与您的WorkRequest对应的WorkInfo实例。- 您可以调用多个返回
WorkInfo的方法之一。如需了解更多信息,请参阅 WorkManager 参考文档。
- 您可以调用多个返回
- getGeneration:在
WorkInfo实例上调用getGeneration()。返回的Int对应于WorkRequest的代次。- 请注意,没有代次字段或属性,只有
WorkInfo.getGeneration()方法。
- 请注意,没有代次字段或属性,只有
跟踪代次示例
以下是上述用于检索 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()
更新工作的策略
以前,更新周期性工作的推荐解决方案是使用 ExistingPeriodicWorkPolicy.REPLACE 策略入队一个 PeriodicWorkRequest。如果存在具有相同唯一 id 的待处理 PeriodicWorkRequest,则新的工作请求将取消并删除它。此策略现已弃用,取而代之的是使用 ExistingPeriodicWorkPolicy.UPDATE 的工作流程。
例如,当将 enqueueUniquePeriodicWork 与 PeriodicWorkRequest 一起使用时,您可以使用 ExistingPeriodicWorkPolicy.UPDATE 策略初始化新的 PeriodicWorkRequest。如果存在具有相同唯一名称的待处理 PeriodicWorkRequest,WorkManager 将其更新为新的规范。按照此工作流程,无需使用 updateWork()。