10、WorkManager:WorkManager介绍、基本用法、链式任务、与JobScheduler的关系

各位同学,今天我们来聊聊WorkManager。说实话,这个组件是我在Android开发中越来越离不开的一个工具。你想想看,现在的App哪个不需要在后台干点活?上传日志、同步数据、定期清理缓存……这些任务如果处理不好,轻则被系统杀掉,重则被用户卸载。

WorkManager就是Google专门为解决这些问题推出的。它不是什么新东西,但它的设计思路,我个人觉得非常优雅。

WorkManager是什么?

简单说,WorkManager是一个用于可延迟、可靠的后台任务的API。注意这两个关键词:

  • 可延迟:不要求立即执行,可以等网络好了、电量充足了再干
  • 可靠:即使App被杀死、手机重启,任务也不会丢

我在项目中遇到过这样一个场景:用户拍照后需要上传原图。如果直接用Service,Android 8以后后台限制越来越严,经常被杀。用WorkManager就简单了——丢给它,它自己会找合适的时间上传,用户完全无感知。

核心特点:

  • 兼容API 14+,不需要考虑版本适配
  • 支持一次性任务和周期性任务
  • 支持链式任务(串行/并行)
  • 自带约束条件(网络、电量、存储等)
  • 任务持久化,重启后继续执行

基本用法:三步搞定一个任务

WorkManager的使用流程非常清晰。我习惯把它分成三步:定义任务 → 配置约束 → 提交执行

第一步:创建Worker类

Worker就是你的任务逻辑。继承Worker,重写doWork()方法:

class UploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): Result {
        // 这里写你的后台任务逻辑
        val imageUri = inputData.getString("image_uri") ?: return Result.failure()

        return try {
            uploadImage(imageUri)
            Result.success()
        } catch (e: Exception) {
            // 如果失败,可以重试
            Result.retry()
        }
    }
}

嗯,这里要注意:doWork()是在后台线程执行的,所以你可以放心做网络请求、数据库操作。但千万别在这里做UI操作。

第二步:配置WorkRequest

WorkRequest是任务的"配置单"。你可以设置约束条件、输入数据、重试策略等:

val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
    .setInputData(workDataOf("image_uri" to "/sdcard/photo.jpg"))
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)  // 需要网络
            .setRequiresBatteryNotLow(true)                 // 电量不能太低
            .build()
    )
    .setBackoffCriteria(
        BackoffPolicy.EXPONENTIAL,
        10, TimeUnit.SECONDS
    )
    .build()

我曾经踩过一个坑:没有设置重试策略,结果任务失败后直接放弃了。后来加上setBackoffCriteria,失败后指数退避重试,稳定多了。

第三步:提交给WorkManager

WorkManager.getInstance(context).enqueue(uploadRequest)

就这一行,剩下的交给系统。WorkManager会考虑各种约束条件,在合适的时候执行你的任务。

我的建议:如果任务需要用户等待结果,别用WorkManager。它适合"后台默默干活"的场景。需要即时反馈的,用Coroutine或RxJava更合适。

链式任务:让任务排队干活

实际项目中,任务往往不是孤立的。比如:先下载图片 → 压缩图片 → 上传图片。这三个步骤有先后依赖关系。

WorkManager的链式任务就是解决这个问题的。说白了,就是把多个任务串起来,一个接一个执行。

串行链:一个接一个

val download = OneTimeWorkRequestBuilder<DownloadWorker>().build()
val compress = OneTimeWorkRequestBuilder<CompressWorker>().build()
val upload = OneTimeWorkRequestBuilder<UploadWorker>().build()

WorkManager.getInstance(context)
    .beginWith(download)        // 第一个任务
    .then(compress)             // 第二个任务
    .then(upload)               // 第三个任务
    .enqueue()

这样,download执行完才会执行compress,compress执行完才会执行upload。前一个任务的输出可以作为后一个任务的输入。

并行链:一起干

有时候任务之间没有依赖关系,可以同时执行。比如同时下载多张图片:

val download1 = OneTimeWorkRequestBuilder<DownloadWorker>().build()
val download2 = OneTimeWorkRequestBuilder<DownloadWorker>().build()
val download3 = OneTimeWorkRequestBuilder<DownloadWorker>().build()

WorkManager.getInstance(context)
    .beginWith(listOf(download1, download2, download3))  // 并行执行
    .then(uploadAll)                                      // 都完成后执行这个
    .enqueue()

你想想看,这种组合方式是不是很灵活?我在做一个相册备份功能时,就是先用并行链下载所有图片,再用串行链逐张上传。代码结构非常清晰。

注意:链式任务中,如果某个任务返回Result.failure(),整个链会终止。如果你希望失败后继续执行后面的任务,需要自己处理异常逻辑。

与JobScheduler的关系

很多同学会问:有了JobScheduler,为什么还要用WorkManager?

这个问题问得好。我简单梳理一下:

特性 JobScheduler WorkManager
最低API版本 API 21+ API 14+
任务持久化 不支持(重启丢失) 支持(Room持久化)
链式任务 需要自己实现 原生支持
观察任务状态 LiveData不支持 支持LiveData
兼容性 不同厂商有差异 统一行为

说白了,WorkManager是JobScheduler的"升级版"。它底层会根据系统版本自动选择实现方式:

  • API 23+:使用JobScheduler
  • API 14-22:使用BroadcastReceiver + AlarmManager

你不需要关心这些细节,WorkManager帮你搞定了。我记得早期做项目时,为了兼容不同版本,我得写一堆if-else判断。现在一个WorkManager全搞定,省心多了。

一句话总结:如果你还在用JobScheduler,除非有特殊需求,否则我建议你迁移到WorkManager。它更现代、更强大、更省心。

知识体系总览

下面这张图是我整理的WorkManager核心知识结构,你可以对照着回顾一下:

WorkManager 基本用法 Worker类 → doWork() WorkRequest → 约束条件 enqueue() → 提交执行 链式任务 beginWith() → then() 串行链:一个接一个 并行链:同时执行 与JobScheduler关系 WorkManager是升级版 自动选择底层实现 更强大、更省心 核心:可延迟、可靠、持久化的后台任务 兼容API 14+,自带约束和重试机制

好了,关于WorkManager的核心内容就这些。记住它的定位:可延迟、可靠的后台任务。下次遇到后台任务需求,先想想WorkManager能不能搞定。我个人经验是,90%的场景它都够用,而且比你自己手写Service+AlarmManager要靠谱得多。

避坑指南:我曾经在项目里用WorkManager做即时任务(比如用户点击按钮后立即发送请求),结果发现有时会有几秒延迟。后来才明白,WorkManager的设计初衷就不是"立即执行"。即时任务请用Coroutine或Thread,别让WorkManager背锅。


公众号:蓝海资料掘金营,微信deep3321