Android使用WorkManager周期性执行后台任务

前言

下面是一个 使用 WorkManager 执行定时任务(周期性任务)的完整、可运行的简单示例,适用于 Android + Kotlin 项目。

场景:每 15 分钟从服务器拉取一次数据并打印日志(模拟同步操作)

注意

WorkManager 执行周期性任务,最短的时间间隔是15分钟。

添加依赖

app/build.gradle.kts(或 .gradle)中添加:

1
2
3
dependencies {
implementation("androidx.work:work-runtime-ktx:2.10.2")
}

创建 Worker 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// SyncWorker.kt
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class SyncWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {

companion object {
const val TAG = "SyncWorker"
}

override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
return@withContext try {
// 模拟网络请求或数据库操作
Log.d(TAG, "开始同步数据...")

// 模拟耗时操作(如 API 调用)
Thread.sleep(2000)

Log.d(TAG, "数据同步成功!")
Result.success()
} catch (e: Exception) {
Log.e(TAG, "同步失败", e)
Result.retry() // 可选:失败后重试
}
}
}

💡 使用 CoroutineWorker 自动在后台线程执行,无需手动管理线程。

调度任务

在 Activity 或 Application 中调度任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// MainActivity.kt(或 App 启动时)
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.work.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

schedulePeriodicSync()
}

private fun schedulePeriodicSync() {
// 构建周期性任务(最小间隔 15 分钟)
val syncWorkRequest = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
.addTag("sync_data") // 便于后续查询或取消
.build()

// 提交任务(保证唯一,避免重复注册)
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"unique_sync_work", // 唯一名称
ExistingPeriodicWorkPolicy.KEEP, // 如果已存在,保留旧任务
syncWorkRequest
)

Log.d("MainActivity", "已调度周期性同步任务(每15分钟)")
}
}

监听任务状态(可选)

如果你想在 UI 上显示任务是否正在运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
WorkManager.getInstance(this)
.getWorkInfosByTagLiveData("sync_data")
.observe(this) { workInfoList ->
if (workInfoList.isNotEmpty()) {
val state = workInfoList[0].state
when (state) {
WorkInfo.State.RUNNING -> println("正在同步...")
WorkInfo.State.SUCCEEDED -> println("同步成功")
WorkInfo.State.FAILED -> println("同步失败")
else -> {}
}
}
}

查看日志验证

运行 App 后,在 Logcat 中过滤 SyncWorker,你会看到:

1
2
D/SyncWorker: 开始同步数据...
D/SyncWorker: 数据同步成功!

注意:

  • 首次调用会立即执行一次,然后每 15 分钟执行一次。
  • Doze 模式下(屏幕关闭+静止),实际执行可能延迟到系统维护窗口(约 15 分钟后)。
  • 如果设备厂商(如小米、华为)限制后台活动,任务可能不执行——需引导用户关闭电池优化。

取消任务

1
2
3
4
5
6
7
// 取消唯一周期任务
WorkManager.getInstance(this)
.cancelUniqueWork("unique_sync_work")

// 或按 tag 取消
WorkManager.getInstance(this)
.cancelAllWorkByTag("sync_data")

总结

步骤

  • 添加 work-runtime-ktx 依赖
  • 创建 SyncWorker : CoroutineWorker
  • PeriodicWorkRequestBuilder 构建任务
  • enqueueUniquePeriodicWork 提交(防重复)
  • getWorkInfosByTagLiveData 监听状态(可选)

注意

如需改成 一次性延迟任务,只需将 PeriodicWorkRequestBuilder 改为 OneTimeWorkRequestBuilder 并调用 setInitialDelay()

常用方法

WorkManager.getInstance(Context) 返回的是 WorkManager 的单例实例,它是整个 WorkManager API 的入口点。通过这个实例,你可以调度任务、查询状态、取消任务等。

以下是 WorkManager 实例提供的主要方法分类及简要介绍(基于 androidx.work 2.10+ 版本):

任务添加

  1. enqueue(WorkRequest)
  • 作用:将一个或多个一次性任务加入队列。

  • 参数OneTimeWorkRequest 或其列表。

  • 注意:这个方法也可以添加周期任务,但是不建议。

  1. enqueueUniqueWork(String uniqueWorkName, ExistingWorkPolicy policy, WorkRequest)
  • 作用:以唯一名称提交一次性任务,避免重复。

  • 策略:

    • REPLACE:替换旧任务
    • KEEP:保留旧任务,忽略新任务
    • APPEND:追加到现有任务链末尾
  • 适用场景:防止用户多次点击按钮导致重复上传。

  1. enqueueUniquePeriodicWork(String uniqueWorkName, ExistingPeriodicWorkPolicy policy, PeriodicWorkRequest)
  • 作用:以唯一名称提交周期性任务
  • 策略REPLACEKEEP(无 APPEND)。
  • 注意:周期任务最小间隔为 15 分钟。

周期性任务最短的间隔是15分钟

假如我们想每5分钟执行一次可以这样做

1
2
3
4
5
6
7
8
9
10
11
12
13
workManager.cancelAllWorkByTag("TimingWorker")
val taskList = arrayListOf(0, 5, 10)
for (taskNo in taskList) {
val request = PeriodicWorkRequest.Builder(TimingWorker::class.java, 15, TimeUnit.MINUTES)
.setInitialDelay(taskNo.toLong(), TimeUnit.MINUTES)
.addTag("TimingWorker")
.build()
workManager.enqueueUniquePeriodicWork(
"TimingWorker$taskNo",
ExistingPeriodicWorkPolicy.REPLACE,
request
)
}

任务查询

所有查询方法都提供 LiveDataListenableFuture 两种版本(如 getWorkInfos...()getWorkInfos...LiveData())。

  1. getWorkInfoById(UUID id)
  • 获取指定 ID 的任务信息(同步或异步)。
  1. getWorkInfosByTag(String tag)
  • 根据标签查询所有匹配的任务。
  1. getWorkInfosForUniqueWork(String uniqueWorkName)
  • 查询通过 enqueueUniqueWork 提交的唯一命名任务。
  1. getWorkInfosByTagLiveData(String tag)
  • 返回 LiveData<List<WorkInfo>>,用于在 UI 中自动监听任务状态变化(推荐用于 Activity/Fragment)。

任务取消

  1. cancelWorkById(UUID id) 取消指定 ID 的任务。

  2. cancelAllWorkByTag(String tag) 取消所有带指定标签的任务。

  3. cancelUniqueWork(String uniqueWorkName) 取消通过唯一名称提交的任务。

  4. cancelAllWork() ⚠️ 谨慎使用:取消所有由当前应用提交的 WorkManager 任务。

任务链构建

  1. beginWith(OneTimeWorkRequest request)
  • 开始构建一个任务链,返回 WorkContinuation
  • 后续可调用 .then(...).enqueue()
  1. beginWith(List<OneTimeWorkRequest>)
  • 并行启动多个任务作为链的起点。

💡 示例:

1
workManager.beginWith(download).then(process).enqueue()

其他实用方法

  1. pruneWork()

    清理已完成(成功/失败)且不再需要保留的内部数据库记录,释放存储空间。

  2. createCancelPendingIntent(UUID id)

    创建一个 PendingIntent,可用于通知栏“取消”按钮直接取消任务(常与前台服务结合使用)。

  3. getForegroundInfo(UUID id)

    (仅限 ListenableWorker)获取前台服务所需的通知信息(用于长时间运行任务)。

注意事项

  • 所有方法都需要传入有效的 Context(通常用 applicationContext 避免内存泄漏)。
  • 不要缓存 WorkManager 实例,每次都调用 getInstance(context) 即可(内部是单例)。
  • 在 Android 15+ 中,后台任务受更严格限制,确保任务满足约束条件或使用前台服务 。

总结

方法类别 常用方法 用途
调度 enqueue, enqueueUniqueWork 提交一次性任务
enqueueUniquePeriodicWork 提交周期任务
查询 getWorkInfoById, getWorkInfosByTag 获取任务状态
...LiveData() 实时监听状态变化
取消 cancelWorkById, cancelAllWorkByTag 取消任务
链式 beginWith(...).then(...).enqueue() 构建依赖任务链
工具 pruneWork, createCancelPendingIntent 清理 & 取消辅助

建议优先使用 LiveData 监听状态 + enqueueUniqueWork 防重复 的组合模式。