Appearance
异步后台任务、保活、系统能力
一句话定位
这一模块不再把 WorkManager、Foreground Service、Flutter、iOS 背景能力平铺罗列,而是按知识导航树串成一条业务落地主线:
Android 主线 → Flutter 边界 → iOS 约束 → 业务落位
读完后,应该能先判断任务是否属于 Android 可靠后台能力,再看 Flutter 只是调用边界还是执行主体,最后用 iOS 约束收口成能真正上线的产品方案。
模块目标
建立 Android / Flutter / iOS 在后台任务、持续任务、系统调度、保活边界上的现实认知,重点不是“列能力”,而是回答:
- 这个任务到底该挂在哪一层?
- 一句话答:先按“Android 可靠能力 → Flutter 调用 / 表达层 → iOS 约束”判断任务归属,再谈落位。
- 哪些能力是 Android 主线可以可靠承接的?
- 一句话答:WorkManager(延迟可恢复)、Foreground Service(持续用户感知)、AlarmManager(定时边界,受限制)等系统级能力。
- Flutter 自己能描述什么,不能替代什么?
- 一句话答:Flutter 能表达业务意图、调用原生能力,但不能替代宿主对后台执行窗口的授权。
- iOS 会在哪一步直接砍掉你的乐观假设?
- 一句话答:在“无通用强保活、后台窗口由系统决定”这一步,依赖准点或持续执行的设计会被砍掉。
- 录音、下载、上传、推送、同步这些任务最终应该如何落位?
- 一句话答:按能力树归位:Android 用系统能力承接执行,Flutter 做调用层,iOS 收缩为机会窗 + 服务端兜底。
模块成熟度与当前缺口
当前模块可视为:L2.5 → L3 过渡中。
- 已有:Android / Flutter / iOS 三层判断框架 + 录音转写业务总案例
- 已有:WorkManager / FGS / Push / Download 等主线专题
- 已补:
recording-sync-state-machine、recording-upload-handoff、push-miss-sync-recovery三个最小闭环样板
换句话说,本模块最重要的后续工作不是再开一批后台 API 词条,而是把已经写出来的判断框架真正落到可运行样板。
先记住一张判定图:后台任务不是一个问题
例子 1:会议录音转写为什么不能被当成“一个后台任务”?
- 错误想法:录音、上传、转写、回流都算“后台录音功能”,丢给一个前台服务 / 一个 Flutter plugin 统一解决。
- 正确拆法:
- 录制阶段:持续、用户可感知 → Android 前台服务 / iOS 合法音频模式
- 上传阶段:可恢复、可能断网 → WorkManager / 系统允许的后台上传模型
- 转写阶段:长耗时、可重试 → 服务端任务状态机
- 回流阶段:尽快看到结果但不承诺秒达 → 推送 + 前台补拉 + 机会窗同步
如果把四段混成一个任务,Android 还能“勉强跑”,iOS 会直接暴露设计错误。
例子 2:为什么“silent push 到了就同步”经常上线后失真?
- 表面现象:测试机能收到静默推送,线上部分用户却迟迟不同步
- 根因:silent push 只是“系统可能给你一个机会窗”,不是后台长期执行许可证
- 正确处理:推送只做“加速看见结果”,真正一致性建立在服务端状态机 + 前台补拉 + 下次启动补同步上
本模块要回答的问题
- WorkManager 是什么,不是什么?
- 一句话答:是延迟、可恢复、带约束感知的后台任务调度器;不是精准定时器,也不保证立即执行。
- Foreground Service 和 WorkManager 的边界在哪里?
- 一句话答:前台服务要求常驻通知、用户可见,适合持续长任务;WorkManager 适合延迟可恢复任务,由系统在合适时机调度。
- iOS 后台音频、后台刷新、silent push 的真实边界是什么?
- 一句话答:都是受限能力:音频需声明且播放中,后台刷新 / 静默推送的时机由系统决定,不能当可靠保活。
- Flutter 自己能做什么,必须借原生做什么?
- 一句话答:Flutter 能组织 UI 与业务逻辑,后台执行 / 保活必须借原生宿主能力(plugin / channel 下放)。
- 录音、下载、推送、同步这些任务应该怎么落位?
- 一句话答:按平台能力树落位:系统级能力承接执行,Flutter 表达意图,iOS 按保守边界收口。
知识导航树:从 Android 能力到业务落位
第一层:Android 主线(先建立可靠后台能力心智)
先读:WorkManager / ForegroundService / AlarmManager 对比
这一层解决:
- WorkManager / Foreground Service / AlarmManager 分别适合什么任务
- 哪些任务属于延迟可恢复、哪些属于持续用户感知、哪些涉及定时边界
- Android 电池优化、厂商限制、后台冻结为什么会改变任务存活率
- “协程 / 线程 / 异步”为什么不等于“可靠后台能力”
Android 主线深入专题:
- Android Foreground Service 深挖(服务类型、通知约束与高版本后台限制) — FGS 类型 / 通知 / 后台启动限制
- Doze 深度休眠模式与国内厂商后台限制 — Doze / 电池优化 / 厂商后台管理
- 移动端推送通道、长连接与长效保活边界 — FCM / 厂商通道 / 长连接与保活边界
- 大文件下载、断点续传与多线程分片下载架构 — 系统下载器 / 断点续传 / 分片 / 校验
适合带着这些问题进入:
- 任务退后台后为什么经常不稳定?
- 一句话答:后台冻结、电池优化、厂商限制会暂停或限制执行;稳定性取决于系统对后台任务的容忍度,不是代码写得对就稳定。
- 为什么短任务不该滥用 Foreground Service?
- 一句话答:前台服务要求常驻通知、用户可见、资源开销大;短任务滥用等于过度承诺,反而更容易被系统收紧。
- 为什么精确定时要先谈系统政策,而不是先写调度代码?
- 一句话答:系统政策(Doze、厂商白名单、省电策略)决定能否准点执行,代码写得再好也改变不了系统调度约束。
第二层:Flutter 边界(再判断 Flutter 只是调用层还是执行层)
这一层解决:
- Flutter 自己在后台能力上能做什么、不能做什么
compute/ isolate 为什么只能解决 CPU 与隔离,不解决系统授权- Flutter plugin / channel 如何把任务下放到原生后台能力
- 为什么跨端层适合表达业务意图,但不应假装自己拥有宿主级后台能力
适合带着这些问题进入:
- 这件事是 Flutter 可以独立完成,还是必须桥接原生?
- 一句话答:凡涉及宿主后台执行授权 / 保活的都必须桥接原生;Flutter 只做调用层与业务表达。
- isolate 能不能替代 WorkManager / Foreground Service?
- 一句话答:不能:isolate 只解决 CPU 计算与隔离,不解决系统是否授予后台执行窗口。
- Flutter 页面退下去后,为什么 Dart 代码不等于还拿着后台执行窗口?
- 一句话答:页面退出后 App 可能被挂起或回收,Dart 代码能否继续跑取决于宿主进程是否存活、系统是否给执行窗口。
第三层:iOS 约束(最后用最严格平台收口设计)
最后读:iOS 后台能力现实边界
这一层解决:
- iOS 不存在通用强保活的现实边界
- 后台音频、定位、下载、silent push、Background Fetch 到底各能承载什么
- 为什么 BGTask / 后台刷新不是准点调度器
- 跨端方案为什么必须以 iOS 最保守约束反推产品能力
适合带着这些问题进入:
- Android 能成立的后台模型,为什么在 iOS 上不成立?
- 一句话答:iOS 没有通用强保活,后台执行靠白名单例外与机会窗,Android 的“后台模型”心智在 iOS 直接失效。
- 哪些能力是白名单例外,哪些只是机会窗?
- 一句话答:后台音频、定位、VoIP 等是声明式例外;后台刷新、silent push 只是系统给的“机会窗”,不保证触发。
- 产品承诺“持续同步 / 准点执行”时,跨端设计应该如何降级?
- 一句话答:降级为“尽力而为 + 下次启动补同步”或服务端拉取兜底,不承诺准点或持续执行。
第四层:业务落位(把能力树变成真实方案)
最后收口到业务方案,而不是停在 API 名词。
先读业务案例专题页:
- 录音转写:录制进行中 + 断点上传 + 会话同步 + 推送回流 —— 以“会议录音转写”为主线,串起录制、上传、转写、结果回流、推送唤醒与多端同步
这一层的判断框架依然成立:
- 录音类:先问是否需要持续用户可感知,再判断前后台差异
- 下载 / 上传类:先问是否要求可靠完成、断点续传、系统托管
- 同步类:先问是机会型刷新,还是必须准点 / 必达
- 推送唤醒类:先问平台是否真的授予唤醒窗口,而不是把消息通道当作常驻保活
推荐阅读路径:按决策链而不是按知识点平铺
路径 A:先做 Android 方案,再判断跨端能否成立
- WorkManager / ForegroundService / AlarmManager 对比
- Flutter 后台能力边界
- iOS 后台能力现实边界
- 回到本页“业务落位树”,确认产品方案是否要降级
路径 B:先查“Flutter 能不能自己做后台任务”
路径 C:先查“跨端业务为什么上线后不稳定”
- iOS 后台能力现实边界
- WorkManager / ForegroundService / AlarmManager 对比
- Flutter 后台能力边界
- 再回到业务落位,决定是否改产品承诺或服务端配合
导航总表:每一层读什么
| 导航层 | 先回答什么 | 对应文档 | 关键判断 |
|---|---|---|---|
| Android 主线 | 任务该交给哪种 Android 后台能力 | WorkManager / ForegroundService / AlarmManager 对比 | WorkManager / Foreground Service / AlarmManager 的职责边界 |
| Flutter 边界 | Flutter 是执行主体,还是原生后台能力的调用层 | Flutter 后台能力边界 | isolate 不等于后台授权,plugin / channel 才能对接宿主能力 |
| iOS 约束 | 跨端方案落到 iOS 时哪些假设会失效 | iOS 后台能力现实边界 | iOS 没有通用强保活,BGTask / silent push 都有严格现实边界 |
| 业务落位 | 录音 / 下载 / 上传 / 同步 / 推送到底怎么设计 | 录音转写:录制进行中 + 断点上传 + 会话同步 + 推送回流 | 先拆业务阶段,再看系统授权、服务端状态机与结果回流 |
业务落位树:常见任务怎么顺着约束落地
1. 录音
- 先看 Android 主线:是否需要持续、用户可感知、前台通知常驻
- 再看 Flutter 边界:Flutter 页面可做控制层,但实际录音存续通常依赖原生能力
- 最后看 iOS 约束:是否符合后台音频类白名单,以及产品是否接受系统更强限制
2. 下载 / 上传
- 先看 Android 主线:是否要可靠完成、断点续传、系统托管
- 再看 Flutter 边界:Dart 逻辑更适合表达状态与交互,真正后台可靠性往往要借原生
- 最后看 iOS 约束:是否必须改成系统允许的下载 / 上传模型,接受执行时机不完全可控
3. 同步 / 刷新
- 先看 Android 主线:是立刻执行、延迟执行,还是周期性机会执行
- 再看 Flutter 边界:不要把 isolate 当成跨平台后台调度器
- 最后看 iOS 约束:默认当机会型刷新处理,不承诺准点与持续存活
4. 推送唤醒
- 先看 Android 主线:推送更多是触发机会,不是无限后台时长许可证
- 再看 Flutter 边界:消息到达后 Flutter 能处理的范围取决于宿主交给它的执行窗口
- 最后看 iOS 约束:silent push 是否真正到达、是否被系统给予执行机会,都不可乐观假设
相近概念不要混
| 概念 | 它回答的问题 | 不回答什么 |
|---|---|---|
| 协程 / Future / isolate | 任务如何并发执行 | OS 是否授予后台执行窗口 |
| WorkManager | 延迟、可恢复、系统调度型后台任务如何运行 | 持续用户可感知任务怎么常驻 |
| Foreground Service | 用户可感知的持续任务如何在 Android 前台服务中存活 | iOS 是否存在等价通用能力 |
| BGTask / Background Fetch | iOS 在特定机会窗里何时可能让你执行 | 是否能准点、长期、稳定保活 |
| plugin / channel | Flutter 如何调用宿主能力 | Flutter 自己是否拥有宿主后台授权 |
与上一模块的衔接
当你还在问“页面离开后为什么又重建 / 为什么对象没释放”时,先回到 ../02-memory-lifecycle/README.md。
当问题已经升级为“页面离开后任务还能不能继续、系统是否允许可靠后台执行”时,再进入本模块。
对应 labs 入口
当前 docs/03-background-tasks/ 已落地两个可运行实验,其余仍在计划补齐:
- workmanager-foregroundservice-demo —— WorkManager 状态流转 + FGS 常驻通知启停
- alarmmanager-doze-demo ——
setExactvssetExactAndAllowWhileIdle、Doze 延迟与精确闹钟边界 - recording-sync-state-machine —— Flutter 会话状态机:录制结束、上传、转写、推送丢失补拉
- recording-upload-handoff —— Android 录音结束后的 uploadSessionId 建立与文件生命周期管理
- push-miss-sync-recovery —— 推送丢失后以前台补拉与多端进入收敛结果
当前最值得优先补的 3 个样板
recording-sync-state-machine- 已落地:Flutter host 演示
recording / waitingUpload / uploading / transcribing / synced / failed状态收敛
- 已落地:Flutter host 演示
recording-upload-handoff- 已落地:Android host 演示 uploadSessionId 建立、失败保留本地文件、成功后再清理
push-miss-sync-recovery- 已落地:shared 脚本演示“推送丢失 → 前台补拉 → 多端进入仍能同步”
复习检查题
为什么这一模块要改成“Android 主线 → Flutter 边界 → iOS 约束 → 业务落位”的导航树?
答:因为后台任务能否成立,首先取决于 Android 等宿主平台提供什么可靠能力;Flutter 更多是调用层和业务表达层;而最终跨端能否上线,往往要以 iOS 最严格约束收口。只有这样排,阅读顺序才与真实技术决策顺序一致。
为什么 Flutter isolate 不能直接当成后台任务方案?
答:因为 isolate 解决的是并发执行与计算隔离,不解决系统是否授予后台执行窗口。没有宿主平台的后台授权,isolate 也无法凭空获得可靠后台时长。
为什么业务落位要放在最后,而不是一开始就按“录音 / 下载 / 同步”分文章?
答:因为业务场景只是表象,不先分清 Android 主线能力、Flutter 边界与 iOS 约束,就会把很多表面相似的任务错误地归为同一种后台方案。先建能力树,再谈业务落位,才能避免方案先天不可落地。
为什么跨端后台设计默认要被 iOS 收紧?
答:因为 iOS 没有 Android 式通用强保活能力,很多后台场景只能依赖白名单例外或机会窗。如果产品承诺建立在 Android 心智上,跨端上线后就会在 iOS 上出现执行时机漂移、任务不触发或能力被系统拒绝的问题。
备注
本模块应优先结合真实业务样本,例如录音、下载、推送、后台同步与电池优化问题;但首页导航先解决“知识怎么走”,专题页再展开机制、参数、误配与排障。