4、WorkRequest详解:OneTimeWorkRequest与PeriodicWorkRequest、WorkRequest的构建器模式、设置任务ID与标签、任务约束条件入门

好,咱们今天来聊聊 WorkRequest。说实话,这是整个 WorkManager 里最核心的概念之一。你想想看,你要让系统帮你干活,总得告诉它「干什么活、什么时候干、有什么要求」吧?WorkRequest 就是干这个的。

我个人习惯把 WorkRequest 理解成「任务订单」。你写好 Worker(干活的人),然后通过 WorkRequest 把订单下给系统。系统拿到订单后,会根据你设置的约束条件,在合适的时机安排执行。

两种 WorkRequest:一次性 vs 周期性

WorkManager 提供了两种任务类型,说白了就是:干一次反复干

类型 类名 适用场景
一次性任务 OneTimeWorkRequest 上传日志、发送通知、数据同步一次
周期性任务 PeriodicWorkRequest 定期清理缓存、每日统计上报、心跳检测

我在项目中遇到过这样一个坑:有个同事把「用户退出登录时清除本地缓存」做成了 PeriodicWorkRequest。结果用户每次退出,系统都以为要定期执行,搞得缓存清了一遍又一遍。嗯,这里要注意——一次性任务就用 OneTimeWorkRequest,别图省事混着用

构建器模式:链式调用真香

WorkRequest 的构建器模式,我个人觉得是 Android 里设计得最舒服的 API 之一。你不需要记住一堆参数的顺序,直接链式调用就行。

// 一次性任务
val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
    .setInputData(workDataOf("key" to "value"))
    .addTag("upload")
    .build()

// 周期性任务(最短间隔15分钟)
val cleanWork = PeriodicWorkRequestBuilder<CleanWorker>(
        15, TimeUnit.MINUTES
    )
    .setConstraints(constraints)
    .addTag("clean")
    .build()

你看,代码读起来就像在说「我要一个上传任务,给它传点数据,打个标签,好了」。这就是构建器模式的好处——可读性强,不容易出错

注意:PeriodicWorkRequest 的最小间隔是 15 分钟。你设成 5 分钟?系统会直接给你改成 15 分钟。我曾经试过设成 1 分钟想做个测试,结果日志死活不按预期跑,查了半天文档才发现这个限制。

任务 ID:给你的任务一个身份证

每个 WorkRequest 创建时都会自动生成一个 UUID。但说实话,UUID 那串东西谁记得住?所以 WorkManager 允许你自定义 ID。

val customId = UUID.fromString("a1b2c3d4-e5f6-7890-abcd-ef1234567890")
val work = OneTimeWorkRequestBuilder<MyWorker>()
    .setId(customId)
    .build()

为什么要自定义 ID?我举个例子。你有个「每日签到」的任务,如果每次生成随机 ID,你就没法判断这个任务是不是已经存在了。用固定 ID,你可以通过 WorkManager.getWorkInfoById() 随时查询状态。

小技巧:我个人习惯用「功能名 + 时间戳」的哈希值作为 ID,既保证唯一性,又能快速定位。

标签:给任务分组管理

标签就像给任务贴的便签。你可以给多个任务贴上同一个标签,然后一次性操作它们。

// 给任务贴标签
val workA = OneTimeWorkRequestBuilder<WorkerA>()
    .addTag("sync_group")
    .build()

val workB = OneTimeWorkRequestBuilder<WorkerB>()
    .addTag("sync_group")
    .build()

// 取消整个组
WorkManager.getInstance(context)
    .cancelAllWorkByTag("sync_group")

我曾经接手过一个项目,里面十几个任务都是单独取消的,代码写得又臭又长。后来我全部加上标签,一行代码就能取消一组任务。标签这东西,前期规划好,后期维护能省不少事

任务约束条件入门

约束条件,说白了就是「什么时候才干活」。你不想在用户刷抖音的时候突然跑个上传任务吧?也不想在电量只剩 5% 的时候做视频压缩吧?

val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED)  // 需要网络
    .setRequiresBatteryNotLow(true)                 // 电量不低
    .setRequiresCharging(true)                      // 正在充电
    .setRequiresStorageNotLow(true)                 // 存储空间充足
    .setRequiresDeviceIdle(true)                    // 设备空闲(API 23+)
    .build()

val work = OneTimeWorkRequestBuilder<BackupWorker>()
    .setConstraints(constraints)
    .build()

这里有个细节我想强调一下:约束条件是「与」的关系。也就是说,所有条件都满足时,任务才会执行。你设了需要网络 + 需要充电,那手机插着电但没网,任务照样不跑。

核心要点:约束条件不是「尽量满足」,而是「必须满足」。系统会一直等待,直到所有条件都成立。如果你的任务一直不执行,先检查约束条件是不是太苛刻了。

嗯,说到这我想起一个真实案例。有个同事给日志上传任务加了 setRequiresDeviceIdle(true),结果用户手机从来没空闲过(一直在刷视频),日志攒了一个月都没传上去。后来我建议他去掉这个约束,或者加个超时兜底策略,问题才解决。

知识体系总览

下面这张图是我自己整理的,把 WorkRequest 的核心知识点串了起来。你看一遍应该就能有个整体印象。

WorkRequest 知识体系 WorkRequest OneTimeWorkRequest PeriodicWorkRequest 适用于:上传、通知、单次同步 最小间隔15分钟 构建器模式 · 任务ID · 标签 · 约束条件 链式调用 · UUID/自定义ID · 分组管理 · 网络/电量/充电/空闲

从图上你能看出来,WorkRequest 就两大分支。但不管哪种类型,构建器模式、ID、标签、约束条件这些公共特性都是一样的。学透这些,你就能应付绝大多数后台任务场景了。

好了,这一节的内容就到这。记住一句话:WorkRequest 是任务订单,Worker 是干活的人,约束条件是干活的前提。把这几个概念理清楚,后面的内容学起来就轻松多了。