Appearance
WorkManager / ForegroundService / AlarmManager 对比
Android 后台任务的关键不在“会不会调 API”,而在 把任务放到正确层级:短任务、可恢复任务、持续用户感知任务、精确定时任务,本来就不该用同一套模型。
代码索引
| 主题 | Lab 说明 | 源码 |
|---|---|---|
| WorkManager 状态流转 + FGS 常驻通知 | workmanager-foregroundservice-demo | BackgroundTasksDemoActivity.kt |
一句话定义
- WorkManager:延迟执行、约束感知、可恢复的后台任务主方案
- Foreground Service:持续运行且用户明确感知的任务
- AlarmManager:特定精确触发场景的系统闹钟能力
为什么需要
Android 后台能力最容易出现两个误区:
- 只会一种工具,然后所有场景都硬套
- 只看“能跑起来”,不看系统限制、用户感知和长期稳定性
结果常见于:
- 后台同步不稳定
- 长任务被系统杀掉
- 滥用前台服务导致体验差
- 精确定时需求错误落在 WorkManager 上
底层机制
0.1 后台任务选型决策图
这张图优先用 Mermaid,因为它本质上是一个 单入口的判断树:读者真正需要的是“先问什么、再落到哪种能力”,而不是复杂排版。
0.2 运行语义对照图
如果你已经大致知道三个 API 的名字,但老是记混它们各自承诺什么,这张对照图可以先把“执行语义”压成一眼能扫完的最小地图。
1. WorkManager:默认后台任务主方案
适合:
- 延迟执行
- 条件约束(联网、充电、空闲)
- app 重启后尽量恢复
- 周期性后台工作
它更像“任务调度框架”,不是“马上后台起个线程”。
最小示例与核心参数(WorkManager 的关键在于"约束 + 重试策略")
kotlin
// 1. 定义任务:继承 CoroutineWorker,在 doWork 里做真正的工作
class SyncWorker(context: Context, params: WorkerParameters) :
CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
syncData() // 联网同步
Result.success() // 成功 → 任务完成
} catch (e: IOException) {
Result.retry() // 可重试错误 → 交给退避策略
}
}
}
// 2. 提交带约束的请求:一次性延迟任务
val request = OneTimeWorkRequestBuilder<SyncWorker>()
.setInitialDelay(15, TimeUnit.MINUTES) // 延迟 15 分钟执行
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 有网才执行
.setRequiresCharging(false) // 不要求充电
.build()
)
.setBackoffCriteria( // 失败重试:指数退避
BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS
)
.build()
WorkManager.getInstance(context).enqueue(request)- 核心参数:
setInitialDelay(延迟多久)、Constraints(联网/充电/空闲等执行条件)、setBackoffCriteria(失败重试策略)、setPeriodic(周期任务,最小间隔 15 分钟)。 - 可能执行顺序:提交 → 满足约束后由系统调度 →
doWork()执行 → 返回success或retry(按退避策略重试)→ app 被杀后任务仍在数据库,下次启动继续。 - 观察重点:WorkManager 不承诺立即执行,也不承诺准点——它保证的是"尽力执行 + 可恢复";需要精确时刻请见 §3 AlarmManager。
2. Foreground Service:持续执行 + 用户可见
对应 Lab:workmanager-foregroundservice-demo(FGS 部分)
适合:
- 录音
- 导航
- 持续定位
- 明确进行中的上传/下载
关键点:
- 必须有通知
- 用户应当理解“为什么它在后台持续跑”
- 不能把它当万能保活工具
3. AlarmManager:少数精确定时场景
适合:
- 明确的时间点提醒
- 闹钟类能力
- 某些必须在指定时刻触发的场景
但要注意:
- 新系统上权限和限制更多
- 不适合拿来做高频后台轮询
Android / Flutter / Web / Backend 对照
| 维度 | Android | Flutter | iOS | Backend |
|---|---|---|---|---|
| 延迟/约束任务 | WorkManager | 需借原生 | BGTask 类机会窗 | 定时任务/队列 |
| 持续感知任务 | Foreground Service | 需借原生 | 受白名单限制 | 常驻进程 |
| 精确定时 | AlarmManager | 需借原生 | 很弱 | cron / scheduler |
| 业务建议 | 分层选型 | Flutter 只做调用侧 | 更保守 | 负责长期稳定执行 |
常见场景
1. 数据同步
通常优先:
- WorkManager
- 结合联网/充电等约束
而不是:
- 常驻前台服务
- 手写无限循环线程
2. 录音 / 导航 / 持续定位
这类一般更像:
- Foreground Service
- 用户明确知道任务在进行
3. 到点提醒
更接近:
- AlarmManager
- 配合通知/广播
而不是让 WorkManager 去模拟“精确闹钟”。
常见坑
| 坑 | 现象 | 修法 |
|---|---|---|
| 用 WorkManager 做强实时持续任务 | 时机不稳定 | 换 FGS 或调整产品要求 |
| 用 FGS 做普通同步 | 通知打扰、系统策略风险 | 普通后台工作优先 WorkManager |
| 用 AlarmManager 做高频轮询 | 电量和系统策略问题 | 改成服务端推送/批量同步/任务调度 |
| 只看单次 demo 能跑 | 线上长期不稳定 | 按系统模型重新分类任务 |
与相近概念对比
| 能力 | 更像什么 |
|---|---|
| WorkManager | 可恢复任务调度器 |
| Foreground Service | 用户看得见的持续执行任务 |
| AlarmManager | 系统闹钟 / 指定时刻触发 |
对应实验
| Lab | 说明 | 源码 |
|---|---|---|
| workmanager-foregroundservice-demo | WorkManager 状态流转(ENQUEUED→RUNNING→SUCCEEDED/RETRY)+ FGS 常驻通知启停 | BackgroundTasksDemoActivity.kt · SyncWorker.kt · DemoForegroundService.kt |
复习检查题
为什么 WorkManager 适合作为普通后台任务主方案?
答:因为它支持约束感知、延迟执行、重启恢复和统一调度,适合大多数“需要后台完成但不要求持续实时运行”的任务。
为什么 Foreground Service 不能滥用?
答:因为它要求用户可见通知,且语义上应对应明确进行中的任务;滥用会造成体验和系统策略问题。
为什么 AlarmManager 不适合当高频后台轮询器?
答:因为它面向特定精确定时场景,系统限制多,滥用会带来电量与触发稳定性问题。
速记
- 普通后台任务:先想 WorkManager
- 持续可感知任务:FGS
- 精确定时:AlarmManager
- 不要一种工具打天下