本文档整理了 Android Jetpack
WorkManager的几种典型使用方式:同步阻塞任务、协程挂起任务、Function函数式接口,以及任务状态映射的实现思路。适用场景:需要在应用退出或重启后仍可调度/执行后台任务(如日志上传、数据同步、定时备份等)。
一、简单的同步阻塞任务
说明 :Worker 是 WorkManager 最基础的实现方式,doWork() 在 后台线程 中同步执行,方法返回时任务即结束。返回值 Result 表示成功 (success())、失败 (failure()) 或可重试 (retry())。由于运行在 Executor 上,不能 直接在 doWork() 里调用挂起函数(suspend),否则会阻塞线程池。
kotlin
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.Result
class UploadWorker(
appContext: Context,
workerParams: WorkerParameters
) : Worker(appContext, workerParams) {
override fun doWork(): Result {
return try {
// 执行耗时操作,例如上传文件
val imageUrl = inputData.getString("image_url")
uploadImage(imageUrl)
// 返回成功
Result.success()
} catch (e: Exception) {
// 返回失败,可根据配置重试
Result.failure()
}
}
private fun uploadImage(url: String?) {
}
}
二、支持挂起函数
说明 :当业务里已经有大量 suspend 函数(例如网络请求、数据库读写)时,使用 CoroutineWorker 可以直接复用挂起代码而无需 runBlocking。doWork() 是 suspend 函数,WorkManager 内部会在 Dispatchers.Default 上调度,不会阻塞主线程。
kotlin
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.google.common.util.concurrent.ListenableFuture
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class UploadCoroutineWorker(
appContext: Context,
workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
// 直接调用挂起函数,无需担心线程切换
override suspend fun doWork(): Result {
return try {
val imageUrl = inputData.getString("image_url")
uploadImage(imageUrl)
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
private fun uploadImage(url: String?) {
}
}
三、函数式接口
说明 :最常见的「输入→输出」转换接口。下面的例子把 Integer 转成带前缀的 String
csharp
public interface Function<I, O> {
O apply(I input);
}
例子:
typescript
Function<Integer, String> intToString = new Function<Integer, String>() {
@Override
public String apply(Integer input) {
return "Number is: " + input;
}
};
// 使用函数
String result = intToString.apply(10);
System.out.println(result); // 输出: Number is: 10
函数式接口真实落地案例
说明 :WorkManager 在拿到一组任务 ID 后,会先查询数据库中的 WorkSpec.WorkInfoPojo,再用 WORK_INFO_MAPPER 把数据库对象转换成对外暴露的 WorkInfo。这里的 Function<List<WorkInfoPojo>, List<WorkInfo>> 通过 Function 把数据层模型映射为业务层模型,再由 WorkManager 内部把结果投递到线程池上执行后续逻辑(如状态回调、通知等)。
ini
public List<WorkInfo> runInternal() {
WorkDatabase workDatabase = workManager.getWorkDatabase();
List<WorkSpec.WorkInfoPojo> workInfoPojos =
workDatabase.workSpecDao().getWorkStatusPojoForIds(ids);
return WorkSpec.WORK_INFO_MAPPER.apply(workInfoPojos);
}
WORK_INFO_MAPPER函数猜测可能实现
import androidx.work.Data;
import androidx.work.WorkInfo;
import androidx.work.impl.model.WorkSpec;
import androidx.work.impl.utils.StringCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
// 假设这是 WorkSpec 类内部的静态字段或方法
public static final Function<List<WorkSpec.WorkInfoPojo>, List<WorkInfo>> WORK_INFO_MAPPER =
new Function<List<WorkSpec.WorkInfoPojo>, List<WorkInfo>>() {
@Override
public List<WorkInfo> apply(List<WorkSpec.WorkInfoPojo> input) {
if (input == null || input.isEmpty()) {
return new ArrayList<>();
}
List<WorkInfo> workInfos = new ArrayList<>(input.size());
for (WorkSpec.WorkInfoPojo pojo : input) {
// 1. 解析 UUID
UUID id = UUID.fromString(pojo.id);
// 2. 获取状态
WorkInfo.State state = pojo.state;
// 3. 解析输入数据
Data inputData = Data.fromByteArray(pojo.inputData);
// 4. 解析输出数据
Data outputData = Data.fromByteArray(pojo.outputData);
// 5. 获取标签列表
List<String> tags = StringCompat.split(pojo.tags, ",");
// 6. 获取运行尝试次数
int runAttemptCount = pojo.runAttemptCount;
// 7. 构建 WorkInfo 对象
// 注意:WorkInfo 的构造函数在不同版本可能有差异,以下为典型构造方式
WorkInfo workInfo = new WorkInfo(
id,
state,
inputData,
tags,
outputData,
runAttemptCount
);
workInfos.add(workInfo);
}
return workInfos;
}
};
五、Request 请求的完整执行流程
说明 :从调用方 WorkManager.enqueue(request) 一直到 Worker doWork() 真正运行、再到 WorkInfo 状态对外暴露,整个链路涉及 构建请求 → 持久化 → 平台调度 → 任务包装 → 线程池执行 → 结果回写 → 状态监听 七个阶段。理解这条链路是排查「任务没跑、跑多次、状态不对」等问题的基础。
1. 构建 Request
WorkRequest 是请求的最小单元,常用实现是 OneTimeWorkRequest(一次性)和 PeriodicWorkRequest(周期性)。可以叠加 Constraints(网络、电量、充电等约束)、Data(输入数据)、BackoffPolicy(重试策略)。
scss
val request = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(workDataOf("image_url" to "https://x/1.jpg"))
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
.addTag("upload")
.build()
2. enqueue → 写入数据库
WorkManager.enqueue() 只做一件事:把 WorkRequest 序列化成 WorkSpec,通过 Room 写入本地 SQLite(androidx.work.workdb),并打上 ENQUEUED 状态。
less
// WorkManagerImpl#enqueue(简化)
public Operation enqueue(@NonNull WorkRequest request) {
WorkSpec workSpec = request.getWorkSpec(); // 把 Request 转为持久化模型
WorkDatabase db = mWorkManager.getWorkDatabase();
db.workSpecDao().insertWorkSpec(workSpec); // 落库
mWorkManager.getProcessor().startWork(...); // 通知处理器
}
3. 平台级调度器按约束派发
数据库写入后,多个调度器轮询待执行任务,按约束挑选「现在能跑」的交给系统:
scss
// SystemJobScheduler#schedule(简化)
public void schedule(WorkSpec... workSpecs) {
JobInfo jobInfo = new JobInfo.Builder(id, componentName)
.setRequiredNetworkType(workSpec.constraints.networkType)
.setRequiresCharging(workSpec.constraints.requiresCharging)
.setMinimumLatency(workSpec.calculateInitialDelay())
.setBackoffCriteria(workSpec.backoffPolicy, workSpec.backoffDelay)
.build();
jobScheduler.schedule(jobInfo);
}
4. JobService 反向拉起 WorkManager
约束满足后,系统调起 SystemJobService#onStartJob:
typescript
// SystemJobService
@Override
public boolean onStartJob(JobParameters params) {
String workSpecId = ...; // 从 params 拿到要执行的任务 ID
return mWorkManagerImpl.startWork(workSpecId, params); // 交给 WorkManager 内部处理
}
5. WorkManager 内部:处理器 → WorkerWrapper
Processor 维护一个执行队列,把任务包装成 WorkerWrapper,再分配到合适的 Worker 实例:
scss
// Processor#startWork
public boolean startWork(String id, @Nullable JobParameters jobParameters) {
WorkerWrapper wrapper = mWrappers.get(id);
if (wrapper == null) {
wrapper = new WorkerWrapper.Builder(...)
.withWorkerClassName(workSpec.workerClassName) // Worker 类全限定名
.withInputData(workSpec.input)
.withRuntimeExtras(...)
.build();
mWrappers.put(id, wrapper);
}
wrapper.setJobParameters(jobParameters);
return wrapper.run(); // 进入线程池
}
6. 线程池执行(Worker / CoroutineWorker)
- 普通
Worker→Executor线程池执行doWork()(同步阻塞,不能suspend) CoroutineWorker→Dispatchers.Default上跑suspend fun doWork()RxWorker→ 返回Single<T>ListenableWorker→ 返回ListenableFuture
scss
// WorkerWrapper#runWorker(简化)
private void runWorker(@NonNull WorkSpec workSpec) {
WorkerFactory factory = mConfiguration.getWorkerFactory();
Worker worker = factory.createWorkerWithDefaultFallback(
appContext, workSpec.workerClassName, params);
if (worker instanceof CoroutineWorker) {
// 在协程上下文里调用
((CoroutineWorker) worker).startWork().addListener(...);
} else {
// 在 Executor 上调用 doWork
mDefaultExecutor.execute(() -> {
Result result = worker.doWork();
handleResult(result); // 处理返回值
});
}
}
7. 结果回写 → 数据库 → 状态回调
Worker 返回的 Result 被 WorkerWrapper 处理:写回 WorkSpec.state、决定是否需要重试(依据 runAttemptCount 和 BackoffPolicy)、触发依赖链上的下一个任务。
typescript
// WorkerWrapper#handleResult
private void handleResult(Result result) {
switch (result) {
case SUCCESS: markWorkSpecFinished(WorkInfo.State.SUCCEEDED); break;
case FAILURE: markWorkSpecFinished(WorkInfo.State.FAILED); break;
case RETRY: rescheduleWithBackoff(); break;
}
// 通知 Processor 本轮结束,触发下一次调度
mProcessor.onExecuted(mWorkSpecId, result);
}
8. 业务侧监听
前端通过 LiveData / Flow 拿到 WorkInfo,做 UI 提示:
scss
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(request.id)
.observe(this) { info ->
when (info.state) {
WorkInfo.State.ENQUEUED -> log("已入队")
WorkInfo.State.RUNNING -> showProgress()
WorkInfo.State.SUCCEEDED -> toast("上传成功")
WorkInfo.State.FAILED -> toast("上传失败")
WorkInfo.State.BLOCKED -> log("等待约束满足")
WorkInfo.State.CANCELLED -> log("已取消")
}
}