本文最后更新于:1 个月前
使用 WorkManager API 可以轻松地调度即使在应用退出或设备重启时仍应运行的可延迟异步任务。
将 WorkManager 添加到 Android 项目中
要将 WorkManager 库导入到 Android 项目中,请将以下依赖项添加到应用的 build.gradle 文件:
dependencies {
def work_version = "2.3.4"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
}
创建后台任务
任务是使用 Worker
类定义的。doWork()
方法在 WorkManager 提供的后台线程上同步运行。
从 doWork()
返回的 Result
会通知 WorkManager 任务:
- 已成功完成:
Result.success()
- 已失败:
Result.failure()
- 需要稍后重试:
Result.retry()
配置运行任务的方式和时间
Worker
定义工作单元,WorkRequest
则定义工作的运行方式和时间。对于一次性 WorkRequest
,可以使用 OneTimeWorkRequest
,对于周期性工作,可以使用 PeriodicWorkRequest
。
工作约束
Constraints
指明工作何时可以运行。具体约束详见约束的完整列表。
初始延迟
setInitialDelay()
初始延迟,加入队列后至少经过多长时间再运行。
重试和退避政策
如果想要让 WorkManager 重新尝试执行任务,可以从工作器返回 Result.retry()
。
退避延迟时间指定重试工作前的最短等待时间。退避政策定义了再后续重试过程中,退避延迟时间随时间以怎样的方式增长,默认情况下按 BackoffPolicy.EXPONENTIAL
延长。
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
退避政策
BackoffPolicy.EXPONENTIAL
指数方式增长BackoffPolicy.LINEAR
线性增长
定义任务的输入/输出
输入和输出值以键值对的形式存储在 Data
对象中。
输入
// 设置输入数据
val imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)
// 访问输入参数
val imageUriInput = getInputData().getString(Constants.KEY_IMAGE_URI)
输出
Data
也可用于输出返回值,将其添加到 Result.success()
或 Result.failure()
时的 Result()
中。
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)
}
}
标记工作
WorkRequest.Builder.addTag(String)
为任意 WorkRequest
对象分配标识字符串,按逻辑对任务进行分组,就可以对使用特定标记的所有任务执行操作。
工作状态和观察工作
工作状态
BLOCKED
有尚未完成的前提性工作ENQUEUED
工作能够在满足Constraints
和时机条件后立即执行RUNNING
工作器在活跃运行- 终止
State
,只有OneTimeWorkRequest
可以进入这种State
SUCCEEDED
工作器返回Result.success()
。FAILED
工作器返回Result.failure()
。所有依赖工作也会被标记为FAILED
,并且不会运行。
CANCELLED
明确取消尚未终止的WorkRequest
时。所有依赖工作也会被标记为CANCELLED
,并且不会运行。
观察工作
将工作加入队列后,可以通过 WorkManager 检查其状态,相关信息在 WorkInfo
对象中提供,包括工作的 id
、标签、当前 State
和任何输出数据。
WorkManager.getWorkInfoById(UUID)
orWorkManager.getWorkInfoByIdLiveData(UUID)
WorkManager.getWorkInfosByTag(String)
orWorkManager.getWorkInfosByTagLiveData(String)
WorkManager.getWorkInfosForUniqueWork(String)
orWorkManager.getWorkInfosForUniqueWorkLiveData(String)
利用每个方法的 LiveData
变量,可以通过注册监听器观察 WorkInfo
的变化。
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(uploadWorkRequest.id)
.observe(lifecycleOwner, Observer { workInfo ->
if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
displayMessage("Work finished!")
}
})
观察工作器的中间进度
ListenableWorker
支持 setProgressAsync()
API,此类 API 可以保留中间进度。
只有在运行时才能观察到和更新进度信息。
更新进度
对于 ListenableWorker
或 Worker
,setProgressAsync()
会返回 ListenableFuture<Void>
;更新进度是异步过程,因为更新过程包括将进度信息存储在数据库中。在 Kotlin 中,可以使用 CoroutineWorker
对象的 setProgress()
扩展函数来更新进度信息。
class ProgressWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {
companion object {
const val Progress = "Progress"
private const val delayDuration = 1L
}
override suspend fun doWork(): Result {
val firstUpdate = workDataOf(Progress to 0)
val lastUpdate = workDataOf(Progress to 100)
setProgress(firstUpdate)
delay(delayDuration)
setProgress(lastUpdate)
return Result.success()
}
}
观察进度
使用观察工作中的 getWorkInfoBy...()
或 getWorkInfoBy...LiveData()
方法,并引用 WorkInfo
。
WorkManager.getInstance(applicationContext)
// requestId is the WorkRequest id
.getWorkInfoByIdLiveData(requestId)
.observe(observer, Observer { workInfo: WorkInfo? ->
if (workInfo != null) {
val progress = workInfo.progress
val value = progress.getInt(Progress, 0)
// Do something with progress information
}
})
链接工作
WorkManager.beginWith(OneTimeWorkRequest/List<OneTimeWorkRequest>)
返回WorkContinuation
实例。WorkContinuation.then(OneTimeWorkRequest/List<OneTimeWorkRequest>)
,每次调用then()
都会返回一个新的WorkContinuation
实例。WorkContinuation.enqueue()
为WorkContinuation
链排队。
如果添加了 OneTimeWorkRequest
的 List
,这些请求可能会并行运行。
WorkManager.getInstance(myContext)
// Candidates to run in parallel
.beginWith(listOf(filter1, filter2, filter3))
// Dependent work (only runs after all previous work in chain)
.then(compress)
.then(upload)
// Don't forget to enqueue()
.enqueue()
Input Merger
为了管理来自多个父级 OneTimeWorkRequest
的输入,WorkManager
使用 OneTimeWorkRequestBuilder.setInputMerger()
。
OverwritingInputMerger
会尝试将所有输入中的所有键添加到输出中。如果发生冲突,它会覆盖先前设置的键。ArrayCreatingInputMerger
会尝试合并输入,并在必要时创建数组。
取消和停止工作
WorkManager.cancelWorkById(UUID)
重复性工作
PeriodicWorkRequest
无法链接。
val constraints = Constraints.Builder()
.setRequiresCharging(true)
.build()
val saveRequest =
PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
.setConstraints(constraints)
.build()
WorkManager.getInstance(myContext)
.enqueue(saveRequest)
唯一工作
调用 WorkManager.enqueueUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)
或 WorkManager.enqueueUniquePeriodicWork(String, ExistingPeriodicWorkPolicy, PeriodicWorkRequest)
创建唯一工作序列。
第二个参数为冲突解决策略,指定如果已存在具有该唯一名称的未完成工作链,应如何处理。
REPLACE
取消现有工作链,并将其替换为新工作链。KEEP
,保持现有序列并忽略您的新请求。APPEND
在现有序列的最后一个任务完成后运行新序列的第一个任务。
创建唯一工作链 WorkManager.beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)
。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!