一、WorkManager 后台任务:任务链、约束条件与生命周期管理
大家好,我是你们的老朋友。今天我们来聊聊 WorkManager——这个 Android 官方钦定的后台任务解决方案。
说实话,我刚接触 Android 后台任务时,踩过不少坑。Service 被系统杀、JobScheduler 兼容性问题、闹钟管理器不准……直到 WorkManager 出现,我才觉得「嗯,这才是该有的样子」。
WorkManager 的核心价值是什么?说白了,就是帮你把后台任务管得服服帖帖——哪怕 App 被杀了、手机重启了,任务照样能跑。它不是一个简单的线程池,而是一个任务调度框架。
1.1 一次性任务与周期性任务
WorkManager 把任务分成两大类:一次性任务和周期性任务。这个区分很直观,但细节里藏着不少门道。
一次性任务(OneTimeWorkRequest)
一次性任务,顾名思义,只执行一次。我习惯用它来做「数据同步」、「日志上传」这类操作。
val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context).enqueue(uploadWork)
这里有个小细节:enqueue 不是立即执行,而是把任务加入队列。WorkManager 会根据约束条件和系统资源来决定何时运行。
周期性任务(PeriodicWorkRequest)
周期性任务适合「定期清理缓存」、「定时上报位置」这类场景。但要注意——它不保证精确的时间间隔。
val periodicWork = PeriodicWorkRequestBuilder<CleanupWorker>(
15, TimeUnit.MINUTES
)
.setConstraints(
Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build()
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"cleanup",
ExistingPeriodicWorkPolicy.KEEP,
periodicWork
)
1.2 任务链:让任务按顺序执行
实际项目中,任务往往有依赖关系。比如:先下载配置文件,再初始化 SDK,最后上报状态。这时候任务链就派上用场了。
WorkManager 的任务链支持两种模式:顺序执行和并行执行。
顺序任务链
val download = OneTimeWorkRequestBuilder<DownloadWorker>().build()
val init = OneTimeWorkRequestBuilder<InitWorker>().build()
val report = OneTimeWorkRequestBuilder<ReportWorker>().build()
WorkManager.getInstance(context)
.beginWith(download)
.then(init)
.then(report)
.enqueue()
这段代码的意思是:download 执行完 → init 执行 → report 执行。任何一个环节失败,后续任务都会被取消。
并行任务链
有时候多个任务没有依赖关系,可以同时跑。比如同时下载图片和视频。
val imageDownload = OneTimeWorkRequestBuilder<ImageWorker>().build()
val videoDownload = OneTimeWorkRequestBuilder<VideoWorker>().build()
WorkManager.getInstance(context)
.beginWith(listOf(imageDownload, videoDownload))
.then(mergeWork)
.enqueue()
beginWith 可以传入一个 List,表示这些任务并行执行。等它们全部完成后,再执行 then 里的任务。
1.3 约束条件:让任务在合适的时机运行
约束条件是 WorkManager 最强大的特性之一。你可以告诉系统:「这个任务必须在 WiFi 下、电量充足、设备空闲时才能执行」。
| 约束类型 | 说明 | 使用场景 |
|---|---|---|
| NetworkType | 网络类型要求 | 大文件下载需要 WiFi |
| RequiresBatteryNotLow | 电量不能低 | 避免低电量时耗电 |
| RequiresCharging | 需要充电 | 数据备份等耗电操作 |
| RequiresDeviceIdle | 设备空闲 | 后台清理、索引构建 |
| RequiresStorageNotLow | 存储空间充足 | 下载大文件 |
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // 仅 WiFi
.setRequiresCharging(true) // 充电中
.setRequiresDeviceIdle(true) // 设备空闲
.build()
val backupWork = OneTimeWorkRequestBuilder<BackupWorker>()
.setConstraints(constraints)
.build()
1.4 Worker 的输入、输出与进度更新
Worker 不是孤立的。它需要接收数据,返回结果,甚至实时报告进度。
输入数据(InputData)
val inputData = workDataOf(
"url" to "https://example.com/file.zip",
"retry_count" to 3
)
val downloadWork = OneTimeWorkRequestBuilder<DownloadWorker>()
.setInputData(inputData)
.build()
在 Worker 中获取输入:
class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
val url = inputData.getString("url") ?: return Result.failure()
val retryCount = inputData.getInt("retry_count", 0)
// 执行下载逻辑
return Result.success()
}
}
输出数据(OutputData)
Worker 执行完后,可以返回结果给下一个任务:
class DownloadWorker : Worker() {
override fun doWork(): Result {
val filePath = downloadFile()
val output = workDataOf("file_path" to filePath)
return Result.success(output)
}
}
// 下一个 Worker 获取输出
class ProcessWorker : Worker() {
override fun doWork(): Result {
val filePath = inputData.getString("file_path") ?: return Result.failure()
// 处理文件
return Result.success()
}
}
进度更新
这个功能我特别喜欢。你可以实时告诉 UI 层「任务进行到哪了」。
class UploadWorker : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val total = 100
for (i in 1..total) {
// 模拟上传进度
delay(100)
setProgress(workDataOf("progress" to i))
}
return Result.success()
}
}
// 在 Activity/Fragment 中观察进度
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(workRequest.id)
.observe(this) { workInfo ->
val progress = workInfo.progress.getInt("progress", 0)
progressBar.progress = progress
}
setProgress 的数据也要通过 workDataOf 传递,只支持基本类型和 String。
1.5 WorkManager 与 ForegroundService 的对比
很多新手会问:「有了 WorkManager,是不是就不用 ForegroundService 了?」
答案是否定的。它们各有各的战场。
| 对比维度 | WorkManager | ForegroundService |
|---|---|---|
| 生命周期 | 由系统调度,可延迟、可重试 | 由开发者控制,必须显示通知 |
| 系统限制 | 受 Doze 模式影响 | Android 12+ 有前台服务启动限制 |
| 适用场景 | 可延迟的后台任务(同步、备份) | 用户可感知的持续任务(音乐播放、位置追踪) |
| 电量优化 | 系统自动优化 | 需要开发者自行管理 |
| 任务持久化 | 支持(重启后恢复) | 不支持 |
我的建议是:能用 WorkManager 就别用 ForegroundService。但如果你需要做「音乐播放器」或者「实时导航」,那 ForegroundService 还是绕不开的。
1.6 知识体系总览
下面这张图,是我梳理的 WorkManager 核心知识结构。你可以把它当作学习地图:
这张图把 WorkManager 的核心模块串起来了。你从「任务类型」入手,然后理解「任务链」和「约束条件」,再深入「Worker 机制」,最后对比「ForegroundService」——整个知识体系就清晰了。
好了,这一章的内容就到这里。记住,WorkManager 不是万能的,但在 90% 的后台任务场景下,它都是最优解。下一章我们会深入源码,看看 WorkManager 内部是怎么调度任务的——到时候你会发现,它的设计思路真的很巧妙。