当前位置: 首页 > news >正文

Android WorkManager 全面讲解

目录

一、概述

二、核心三大件

2.1 定义 Worker 类

2.1.1 数据的传递

2.1.2 任务的结果

2.1.3 停止与取消

2.2 创建 WorkRequest

2.2.1 任务的类型:一次性 vs. 周期性

2.2.2 调度配置

2.2.3 约束条件

2.2.4 输入数据

2.2.5 添加优先级

2.3 提交任务 WorkManager

2.3.1 提交唯一任务

2.3.2 获取任务状态

(1) 任务状态 (WorkInfo)

(2) 通过 ID 获取(最精确)

(3) 通过 Unique Name 获取(最常用)

(4) 通过 Tag 观察任务状态

2.3.3 取消任务

三、任务链和复杂工作流

3.1 串行执行

3.2 并行执行

3.3 组合任务


系列入口导航:Android Jetpack 概述

一、概述

AndroidWorkManager是 Android Jetpack 组件库的一部分,专门用于处理持久性工作。所谓持久性工作,是指即便应用退出或设备重启,任务也必须执行的情况(例如向服务器同步数据或处理本地数据库)。

它会根据设备的 API 级别和应用状态,自动选择最合适的底层调度方式(如 JobScheduler、Custom AlarmManager 或 BroadcastReceiver)。

添加依赖

dependencies { implementation 'androidx.work:work-runtime:2.9.0' }

二、核心三大件

WorkManager 的基本构成:Worker(定义任务内容)WorkRequest(定义任务如何运行)WorkManager(正式调度任务)

  • Worker (工人):这是你编写逻辑的地方。你继承 Worker 类并实现doWork()方法,告诉系统“要做什么”。

  • WorkRequest (任务请求):这是你的“订单”。你在这里规定任务是一次性的还是周期性的,以及具体的运行规则。

  • WorkManager (管理者):这是系统的指挥官。你把请求交给它,它负责在合适的时机调度执行

2.1 定义 Worker 类

import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; import androidx.work.Worker; import androidx.work.WorkerParameters; public class MyUploadWorker extends Worker { private static final String TAG = "MyUploadWorker"; public MyUploadWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // 执行实际任务(在后台线程运行) try { // 模拟耗时操作 uploadLogs(); return Result.success(); // 任务成功 } catch (Exception e) { Log.e(TAG, "任务失败", e); return Result.failure(); // 任务失败 } // 注意:不推荐使用 Result.retry(),因为会一直重试 } private void uploadLogs() { // 实际的上传逻辑 Log.d(TAG, "开始上传日志..."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Log.d(TAG, "上传完成"); } }

关键点: doWork() 方法运行在后台线程中,所以你可以在这里放心执行耗时操作(比如网络请求或数据库写入),而不会卡死界面。

2.1.1 数据的传递

Worker 并不是孤立运行的。你通常需要给它传参数(比如图片的 URL),同时也可以让它返回结果(比如处理后的文件路径)。有些任务只需要输入,有些只需要输出,而有些则两者兼有

  • 输入数据:在WorkRequest中通过setInputData(Data)设置。这个再后面详细说
  • 获取数据:在Worker内部使用getInputData()
  • 输出数据:在返回Result.success(outputData)携带数据。跟Result.success()不一样

注意:Data对象的大小限制为10KB。它适合传递路径、ID 或简单的标记,不适合传递大型位图或数据库条目。

2.1.2 任务的结果

doWork()必须返回一个 Result,这决定了 WorkManager 下一步怎么做:

结果类型含义
Result.success()任务成功完成。
Result.failure()任务失败,不再重试
Result.retry()任务失败,但告诉系统根据“退避政策”(Backoff Policy)稍后重试

2.1.3 停止与取消

如果用户手动取消了任务,或者约束条件不再满足(比如 Wi-Fi 断了),系统会停止你的 Worker。

  • 你可以通过isStopped()检查任务是否已被停止。
  • 如果你的任务中有循环(比如上传大文件),你应该在循环中不断检查这个标志位,以便优雅地退出。

2.2 创建 WorkRequest

// 一次性任务 OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(MyUploadWorker.class) .build(); // 周期性任务(最小间隔15分钟) PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(MyUploadWorker.class, 15, TimeUnit.MINUTES) .build();

2.2.1 任务的类型:一次性 vs. 周期性

根据执行频率,WorkRequest分为两个子类:

  • OneTimeWorkRequest:用于非重复性工作。

    • 虽然是“一次性”,但如果设置了重试政策,它也可能多次运行

  • PeriodicWorkRequest:用于定期运行的任务(如每晚备份数据)。

    • 限制条件:最小间隔时间为15 分钟

    • 灵活时段 (Flex Interval):你可以指定任务在间隔期内的最后一段时间内运行。

2.2.2 调度配置

在构建请求时,你可以精确控制它的行为:

  • 初始延迟 (Initial Delay): 使用 .setInitialDelay(10, TimeUnit.MINUTES)。即便约束条件已满足,任务也会等待指定时间后再执行

OneTimeWorkRequest delayedWorkRequest = new OneTimeWorkRequest.Builder(MyUploadWorker.class) .setInitialDelay(5, TimeUnit.MINUTES) // 延迟5分钟执行 .build();
  • 指数退避策略 (Backoff Policy): 当Worker 返回 Result.retry()时,系统需要知道多久后重试。

    • Linear:每次重试间隔线性增加

    • Exponential:每次重试间隔指数级增加(例如 10s, 20s, 40s...)。

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyUploadWorker.class) .setBackoffCriteria( BackoffPolicy.EXPONENTIAL, // 退避策略:指数或线性 10, TimeUnit.SECONDS // 初始延迟时间 ) .build();
  • 标记 (Tagging): 通过 .addTag("cleanup") 给任务打标签。这非常有用,因为你可以根据标签一次性取消所有相关的任务,或者观察它们的状态

// 构建请求 WorkRequest statsRequest = new OneTimeWorkRequest.Builder(StatsWorker.class) // 1. 设置初始延迟:注册成功 5 分钟后才真正开始跑 .setInitialDelay(5, TimeUnit.MINUTES) // 2. 打上标签:方便后续管理 .addTag("user_onboarding_tasks") .addTag("priority_low") .build(); // 提交任务 WorkManager.getInstance(context).enqueue(statsRequest); // --- 稍后在其他地方(如退出登录时)--- // 3. 通过标签一次性取消所有相关任务 WorkManager.getInstance(context).cancelAllWorkByTag("user_onboarding_tasks");

2.2.3 约束条件

这是 WorkManager 最强大的地方。你可以定义 Constraints 对象并传给请求:

Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) // 需要网络连接 .setRequiresBatteryNotLow(true) // 电量不低 .setRequiresCharging(false) // 不需要充电 .setRequiresDeviceIdle(false) // 设备不需要空闲 .setRequiresStorageNotLow(true) // 存储空间充足 .build(); OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(MyUploadWorker.class) .setConstraints(constraints) .build();
约束项说明
setRequiredNetworkType比如CONNECTED(有网)、UNMETERED(仅 Wi-Fi)。
setRequiresCharging是否必须连接电源。
setRequiresDeviceIdle是否在设备空闲时运行(适合 CPU 密集型任务)。
setRequiresBatteryNotLow电量不足时是否停止。

2.2.4 输入数据

正如我们之前讨论的,通过setInputData(Data)传递键值对。

Data inputData = new Data.Builder() .putString("user_id", "12345") .putInt("file_count", 10) .putStringArray("file_paths", new String[]{"/path/1", "/path/2"}) .build(); OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyUploadWorker.class) .setInputData(inputData) .build();

2.2.5 添加优先级

这是 WorkManager 2.7 引入的最接近“高优先级”的机制。它告诉系统:这个任务非常重要,应该尽快执行,不受系统能耗优化(如 App Standby Buckets)的严格限制。

OneTimeWorkRequest highPriorityWork = new OneTimeWorkRequest.Builder(MyUploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build();

适用场景:用户触发的必须立刻看到结果的操作(如发送消息、同步重要通知)。

setExpedited中,OutOfQuotaPolicy枚举主要提供以下两个参数

  • RUN_AS_NON_EXPEDITED_WORK_REQUEST:如果加急配额用尽,任务不会被取消,而是会降级为普通的后台任务
  • DROP_WORK_REQUEST:如果加急配额用尽,直接丢弃这个任务请求。

2.3 提交任务 WorkManager

既然我们已经定义好了“工人”(Worker)和“合同”(WorkRequest),现在就到了执行阶段:把请求交给WorkManager

WorkManager workManager = WorkManager.getInstance(context); workManager.enqueue(uploadWorkRequest);
  • WorkManager 是一个单例,你需要通过Context来获取它。
  • 如果你不在乎任务是否重复,直接使用enqueue方法:

2.3.1 提交唯一任务

有时候你不想重复提交同一个任务(比如点击多次“同步”按钮)。你可以通过WorkManager.enqueueUniqueWork()来管理唯一任务:

workManager.enqueueUniqueWork( "unique_upload_name", // 任务的唯一标识名称 ExistingWorkPolicy.REPLACE, // 如果任务已存在,怎么处理?(REPLACE, KEEP, APPEND) (OneTimeWorkRequest) uploadRequest );
  • KEEP:如果已有相同名称的任务在排队,则保留旧的,忽略新的。

  • REPLACE:用新的替换旧的。

  • APPEND:将新任务排在旧任务后面(形成链)。

2.3.2 获取任务状态

在提交任务后,我们通常需要知道任务进行得怎么样了。WorkManager 提供了一套非常强大的机制,让你可以通过任务的 id、tag 或 name 来实时监控任务的状态

WorkManager workManager = WorkManager.getInstance(context); workManager.getWorkInfoByIdLiveData(workRequest.getId()) .observe(this, workInfo -> { if (workInfo != null) { WorkInfo.State state = workInfo.getState(); Data outputData = workInfo.getOutputData(); switch (state) { case ENQUEUED: Log.d("TAG", "任务已入队"); break; case RUNNING: Log.d("TAG", "任务执行中"); break; case SUCCEEDED: Log.d("TAG", "任务成功"); break; case FAILED: Log.d("TAG", "任务失败"); break; case BLOCKED: Log.d("TAG", "任务被阻塞"); break; case CANCELLED: Log.d("TAG", "任务已取消"); break; } } });
(1) 任务状态 (WorkInfo)

当你查询任务状态时,你会得到一个 WorkInfo 对象。它包含了任务当前的所有关键信息:

  • State (状态):任务是正在排队 (ENQUEUED)、运行中 (RUNNING)、已成功 (SUCCEEDED)、已失败 (FAILED),还是被取消了 (CANCELLED)。

  • Output Data (输出数据):如果任务执行成功并返回了结果,你可以从这里拿。

  • Tags (标签):任务关联的标签。

  • Run Attempt Count (运行次数):如果任务失败重试过,这里记录了次数。

你可以根据提交任务的方式选择不同的获取路径

(2) 通过 ID 获取(最精确)

当你提交 WorkRequest 时,每个请求都有一个唯一的 UUID。

// 假设你保存了请求的 ID UUID workId = uploadRequest.getId(); // 使用 LiveData 实时观察 WorkManager.getInstance(context) .getWorkInfoByIdLiveData(workId) .observe(lifecycleOwner, workInfo -> { if (workInfo != null) { System.out.println("当前任务状态: " + workInfo.getState()); } });
(3) 通过 Unique Name 获取(最常用)

如果你使用了enqueueUniqueWork,直接用名字查更方便。

WorkManager.getInstance(context) .getWorkInfosForUniqueWorkLiveData("sync_logs") .observe(lifecycleOwner, workInfos -> { // 注意:唯一任务返回的是一个 List<WorkInfo> for (WorkInfo info : workInfos) { // 处理状态 } });

在使用 getWorkInfosForUniqueWorkLiveData(String uniqueWorkName) 时,即便你认为只运行了一个任务,WorkManager 依然返回一个 List<WorkInfo>

  • 既可以指派给单个任务,也可以指派给一整个任务链
(4) 通过 Tag 观察任务状态
WorkManager.getInstance(context) // 注意:通过 Tag 查询返回的是一个 LiveData<List<WorkInfo>> // 因为一个 Tag 可能对应多个任务请求 .getWorkInfosByTagLiveData("sync_logs") .observe(lifecycleOwner, workInfos -> { if (workInfos == null || workInfos.isEmpty()) { return; } for (WorkInfo workInfo : workInfos) { WorkInfo.State state = workInfo.getState(); System.out.println("任务 ID: " + workInfo.getId() + " 状态: " + state); // 如果你对某个特定状态感兴趣 if (state == WorkInfo.State.SUCCEEDED) { // 处理成功后的逻辑 } } });

2.3.3 取消任务

// 按 ID 取消 workManager.cancelWorkById(workRequest.getId()); // 按标签取消 workManager.cancelAllWorkByTag("upload_tag"); // 取消所有任务 workManager.cancelAllWork();

及时取消不再需要的任务对节省电量和内存至关重要。不过,取消操作并不是“瞬间杀掉”进程,它有一些非常关键的底层逻辑:

当你调用 cancel相关方法时:

  • 如果任务还在排队(ENQUEUED),它会直接变成CANCELLED状态,永远不会被执行。
  • 如果任务正在运行(RUNNING),WorkManager 会向你的Worker 发送一个信号

这是最容易被忽视的一点。WorkManager 无法强行停止你的 Java 代码执行(除非进程被杀),它通过isStopped()标志来通知你。

在任务链(A -> B -> C)中,取消行为具有“连带效应”

  • 如果你取消了 任务 A,那么依赖它的任务 B 和 任务 C 也会自动被标记为 CANCELLED
  • 这非常智能,因为 WorkManager 知道 A 没成功,后面的步骤通常已经没有意义了。

如果你使用的是 enqueueUniqueWork,除了手动调用 cancelUniqueWork("name"),你还可以通过提交一个同名但策略为 REPLACE的新任务来隐式地取消并替换掉旧任务。

取消方式适用场景
cancelWorkById精确控制,比如用户点击了某个特定文件的“取消上传”按钮。
cancelAllWorkByTag批量操作,比如关闭了某个功能模块,需要停掉该模块下所有的后台任务。
cancelUniqueWork业务逻辑控制,确保某个命名的业务流程彻底停止。

三、任务链和复杂工作流

它能让你把多个简单的 Worker像乐高积木一样组合成复杂的业务流。

3.1 串行执行

最简单的形式是:做完 A,再做 B,最后做 C。

WorkManager workManager = WorkManager.getInstance(context); OneTimeWorkRequest workA = new OneTimeWorkRequest.Builder(WorkerA.class).build(); OneTimeWorkRequest workB = new OneTimeWorkRequest.Builder(WorkerB.class).build(); OneTimeWorkRequest workC = new OneTimeWorkRequest.Builder(WorkerC.class).build(); // A -> B -> C 顺序执行 workManager.beginWith(workA) .then(workB) .then(workC) .enqueue();

特点:如果workA 失败,整个链条会停止,B 和 C 都不会运行。

3.2 并行执行

// 同时执行 A 和 B,然后执行 C workManager.beginWith(workA, workB) .then(workC) .enqueue();

在这个场景中,workA 和 workB 会被同时放入调度队列只有当它们两个都成功完成(返回 Result.success())时,workC 才会触发执行。

当 workA 和 workB 并行运行并分别返回数据时,workC 接收到的输入数据(Input Data)会是两者的并集

  • 如果 workA 返回 {"key1": "value1"}
  • 如果 workB 返回 {"key2": "value2"}
  • 那么 workC 通过 getInputData() 就能同时拿到 key1 和 key2。

3.3 组合任务

除了简单的并行,你还可以通过WorkContinuation.combine()多个已经存在的任务链合并在一起。

OneTimeWorkRequest workA, workB, workC, workD; // A 和 B 并行,完成后执行 C,D 等待 A 完成 WorkContinuation continuation1 = workManager.beginWith(workA, workB); WorkContinuation continuation2 = workManager.beginWith(workC); WorkContinuation continuation3 = workManager.beginWith(workD); WorkContinuation combined = WorkContinuation .combine(continuation1, continuation2, continuation3) .then(workE); combined.enqueue();
http://www.jsqmd.com/news/767346/

相关文章:

  • AISMM模型不是万能钥匙?3类不可替代的传统规则引擎场景+混合架构设计图(附2024年金融AI模型淘汰预警清单)
  • R语言AI编程助手gpttools:无缝集成GPT能力,提升数据分析与开发效率
  • 秋天的第一顿大闸蟹,配什么酒才叫绝搭?
  • SQL 第二篇:表结构设计(为什么企业要拆成 3 张表)
  • 5分钟精通明日方舟基建全自动管理:告别繁琐手操,提升效率300%
  • 开源ChatGPT克隆项目实战:架构解析与私有化部署指南
  • 企业内部考试:题库治理比出题更重要
  • 基于DHCPv6的PC自动获取IP地址
  • 高效图片去重清理:AntiDupl.NET开源工具全面指南
  • 2026年智能化的自动去毛刺可靠供应商推荐 - 行业平台推荐
  • 终极指南:5分钟成为Switch游戏文件管理专家
  • 【研报A94】2026年智能原生研究报告:头部底座赋能,垂直场景深耕的新格局
  • 2026年知名宣传片制作公司实力盘点:谁是行业翘楚?
  • ARM Cortex-R82处理器架构与RAS机制详解
  • 基于Alpine的adhocore/phpfpm Docker镜像:生产环境PHP部署优化实践
  • Expo 快速上手
  • Google与英伟达下注!4个月估值40亿,Recursive自学习AI能否改写研究范式?
  • 国外 VPS 账号两步验证 2FA 丢失怎么找回
  • Intel两项关键人事任命:Alex Katouzian、Pushkar Ranade助力客户端计算与物理AI突破
  • 从“能用”到“好用”:优化EasyExcel导入体验,我做了这3件事(含性能考量)
  • C语言学习笔记 - 24.C编程预知识 - 常量以什么样的二进制代码存储在计算机中
  • Ollama桥接器:实现本地大模型与AI应用无缝对接的协议转换方案
  • AI命令行助手aidev:提升开发效率的智能编程副驾实战指南
  • 宏基因组分析实战:用BWA、Bowtie2和Salmon三种工具计算基因丰度,哪个更适合你的数据?
  • 2026年评价高的台州豪车维修保养优选公司推荐 - 行业平台推荐
  • Arm Cortex-R82 AArch64寄存器架构与实时系统优化
  • 别再死记硬背了!用动画图解欧拉筛和埃氏筛,5分钟搞懂核心差异
  • Power BI数据导出新玩法:结合Power Automate与OneDrive,打造个人数据备份流水线
  • Openterface Mini-KVM:经济型USB KVM设备解析与应用
  • 荧光标记蛋白的定制解析——FITC、Cy与罗丹明