Chapter 32: Timer, Wakeup, Scheduling Latency, Power Sensitivity
手机系统里的“时间”同时服务两个目标:让前台交互及时响应,让后台任务在电量和温度预算内完成。一个 App 设置 10 分钟后的同步任务、一个聊天消息点亮通知、一次触摸从黑屏唤醒设备、一个动画帧赶上下一次 VSync,这些现象都经过同一组底层问题:谁负责计时,谁有资格唤醒设备,CPU 从低功耗状态返回要付出多少延迟,系统如何把后台工作合并到更少的唤醒窗口。
本章建立的判断动作是:看到一个“定时任务延迟、通知晚到、触摸响应慢、后台同步被推迟、耗电异常”的现象时,先定位它属于 timer、wakeup source、scheduler latency 还是 power policy,再追踪 App 请求、framework API、系统服务、内核计时器、设备中断和硬件低功耗状态之间的责任边界。
贯穿材料是一条后台同步路径:App 希望每 15 分钟刷新一次轻量数据,同时支持服务端推送即时消息。前台时,用户希望页面立即更新;锁屏静置时,系统希望设备进入 idle 或 suspend;有推送时,modem 或网络栈可能唤醒系统;同步任务本身通常应合并到系统维护窗口。这个例子覆盖 timer、wakeup、scheduling latency 和 power sensitivity 的核心冲突。
公开资料可以支撑本章的边界判断。Linux 文档把高精度计时器、clock event、dynamic tick 放在计时基础设施中说明,见 High resolution timers and dynamic ticks design notes 与 NO_HZ: Reducing Scheduling-Clock Ticks。Linux suspend 文档说明设备中断在 suspend/resume 期间的处理和 wake IRQ 条件,见 System Suspend and Device Interrupts。Android 公开文档说明 WakeLock、Doze、JobScheduler 和 Alarm 的用户态 API 边界,见 PowerManager.WakeLock、Optimize for Doze and App Standby 与 JobScheduler。Apple 公开文档说明 timer 使用、Low Power Mode、QoS 与后台网络延后,见 Minimize Timer Use、React to Low Power Mode on iPhones、Prioritize Work with Quality of Service Classes 与 Defer Networking。Apple iOS 的内部 sleep/wake daemon、驱动策略和调度细节很多属于私有实现,本章只使用公开 API、公开文档、Darwin/XNU 公开概念和平台行为推断。
下面这条链路贯穿全章。它把“15 分钟后台同步”拆成可以检查的系统对象:
图中最关键的判断点是 Service->>Kernel 和 Device-->>Kernel。前者决定“是否需要定时唤醒”,后者决定“是否真的有硬件级唤醒来源”。App 看到的是 callback、通知或延迟结果;系统承担的是计时、唤醒、调度和电源状态之间的取舍。
32.1 Timer 与系统时间管理
Timer 是系统对未来某个时间点或时间间隔的承诺。它接收一个时间条件,登记到系统计时结构里,在条件满足后触发回调、唤醒任务或交给更高层服务处理。手机系统里的 timer 既服务用户可见体验,也影响设备能否进入低功耗状态。
系统时间管理至少包含两类时间。Wall clock 表示用户理解的日历时间,例如今天 8:30 的闹钟;monotonic time 表示单调递增的运行时间,适合超时、动画、网络重试和相对延迟。Wall clock 会受时区、用户改时间、网络校时影响;monotonic time 适合判断“经过了多久”。后台同步、网络超时和动画 frame deadline 通常依赖 monotonic time,闹钟、日历提醒和用户指定时间通常依赖 wall clock。
内核负责把抽象时间转成硬件计时事件。Linux 的高精度计时器把 timer 按过期时间排序,并通过 clock event device 安排下一次中断;传统 timer wheel 更适合大量低精度超时。clock source 提供“现在是什么时间”的读数,clock event device 提供“下一次何时打断 CPU”的能力。对移动系统来说,后一项直接影响功耗:越多精确短周期 timer,CPU 越难长时间停留在深 idle。
| 对象 | 工作定义 | 移动 OS 中的判断用途 |
|---|---|---|
| Wall clock | 用户日历时间 | 判断闹钟、日历、用户指定提醒是否应按本地时间触发 |
| Monotonic time | 单调递增运行时间 | 判断超时、动画节奏、重试间隔和统计耗时 |
| Timer wheel | 面向大量低精度超时的内核结构 | 承载普通 timeout、延后清理和非精确后台工作 |
| hrtimer | 面向高精度过期时间的内核计时器 | 承载精确 sleep、frame deadline、短超时和高精度回调 |
| Alarm | 具有跨 idle 或跨 suspend 语义的系统定时请求 | 连接用户提醒、后台唤醒、维护窗口和权限策略 |
| Timeout callback | 过期后执行的代码路径 | 判断任务是否只是醒来检查状态,还是确实推进用户可见工作 |
贯穿材料里的“每 15 分钟刷新一次轻量数据”看似只是一个定时器,系统视角会先追问三个问题。第一,这个刷新是否用户可见;第二,它是否允许延后;第三,它是否需要把设备从低功耗状态唤醒。答案如果是“刷新首页缓存、允许延后、锁屏时无用户可见结果”,系统会倾向把它交给 JobScheduler、WorkManager、BackgroundTasks 或后台 URL session 一类可批处理机制,而非登记一个高精度周期 timer。
Android 的 API 边界体现了这种分层。AlarmManager 更接近“时间到了要通知系统”的 alarm 入口,JobScheduler 更接近“条件满足时执行工作”的调度入口,WorkManager 在应用开发层包装了可延后工作。Android 6.0 API level 23 引入 Doze 后,锁屏静置、未充电、设备不活动时,普通 alarm、job、sync 和网络访问会被推迟到维护窗口;只有用户可见或具备特殊语义的 alarm 才应使用允许 idle 期间触发的接口。
Apple 平台公开文档也把 timer 当作能耗敏感对象。iOS Energy Guide 建议用系统事件、dispatch source、后台 URL session 或通知替代轮询 timer;必须使用 timer 时,应设置合适 timeout、取消已无用的重复 timer,并设置 tolerance,让系统把多个 timer 合并执行。tolerance 的意义是把“精确在某一毫秒执行”改成“在可接受窗口内执行”,系统因此可以减少 CPU 从 idle 被唤醒的次数。
Timer 判断顺序可以稳定复用:先看时间语义是 wall clock 还是 monotonic;再看精度是否影响用户可见结果;接着看它是否需要跨 idle 或 suspend;最后看系统是否提供批处理、事件通知或后台传输入口。对后台同步这条贯穿路径,合理结论通常是:前台刷新使用普通异步请求和 UI deadline,后台刷新使用可延后任务,用户可见提醒使用 alarm 或通知语义,服务端即时消息使用平台推送通道。
32.2 Wakeup Source 与设备唤醒路径
Wakeup source 是有资格把系统从低功耗状态带回可执行状态的来源。它可以来自硬件中断,也可以来自系统服务登记的 alarm。timer 决定“某个时间点需要处理”,wakeup source 决定“设备睡着时这个事件是否有资格唤醒”。这两个对象必须分开判断。
手机进入低功耗状态后,CPU、总线、外设和电源域可能处在不同层级的 idle、runtime suspend 或 system suspend。设备唤醒路径通常从硬件事件开始:触摸控制器产生中断,充电器状态改变,modem 收到寻呼或数据事件,传感器 hub 达到阈值,RTC alarm 到期。内核需要知道哪个 IRQ 能在 suspend 期间保持唤醒语义,驱动需要在 suspend 前配置硬件,PM core 需要在 resume 阶段恢复设备状态。
Linux suspend 文档区分了“suspend 期间保持 IRQ 可用”和“IRQ 能唤醒系统”。某些特殊中断可以在 suspend/resume 周期保持启用;要让某条中断线具备唤醒能力,还需要驱动或平台代码设置 wake IRQ。这个边界可以帮助读者理解手机现象:一个设备能在运行时产生中断,并不自动表示它能在深度睡眠中唤醒整机。
常见唤醒来源可以按责任主体观察。RTC 或 alarm 来源由系统时间和电源管理共同控制;touch 来源由触摸控制器、显示唤醒策略和输入子系统控制;charger 来源由 PMIC、充电驱动和电源服务控制;sensor 来源常由低功耗传感器 hub 或 motion coprocessor 先处理;modem 来源由基带、RIL 或蜂窝服务链路处理;network 来源在 Wi-Fi 或蜂窝电源状态、系统推送通道和网络栈之间协调。
贯穿材料里的即时消息属于典型唤醒路径。App 维持自己的常驻 TCP 连接,会让 radio、网络栈和应用进程频繁参与唤醒;平台推送把多 App 的实时消息聚合到系统级连接和统一策略中。Android 文档建议在 Doze 和 App Standby 场景下使用 FCM 处理下行消息;普通优先级消息可进入维护窗口,高优先级消息用于时间敏感且用户可见的通知。Apple 平台也通过 APNs 和后台模式限制常驻后台网络,把唤醒资格集中到系统通道和声明过的能力上。
Wakeup source 与 WakeLock 也需要分层。Wakeup source 解决“睡着后谁能叫醒系统”;WakeLock 或 power assertion 解决“系统醒着时某段工作是否需要持续执行”。一个 alarm 可以唤醒设备,但 callback 运行后若工作继续访问网络,系统仍要决定是否给 CPU、radio 和 App 后台执行窗口。一个 WakeLock 可以延缓 suspend,但它通常无法把一个已进入深度睡眠的设备直接叫醒。
排查“通知晚到”时,先看服务端是否走平台推送;再看消息优先级是否对应用户可见通知;接着看设备是否处于 Doze、Low Power Mode、弱网或省电策略;最后看 App 是否依赖后台轮询。这个顺序把责任边界拆开:服务端负责消息提交,平台推送负责统一连接,系统电源策略负责唤醒预算,App 负责在短窗口内完成用户可见处理。
32.3 Scheduling Latency 与交互响应
Scheduling latency 是事件已经具备执行条件后,目标线程真正获得 CPU 之前的等待时间。timer 到期、IRQ 到达、Binder 或 XPC 回调可用,都只表示“事件进入系统”;线程还要经过中断处理、软中断或 workqueue、run queue 排队、优先级选择、CPU idle 退出和可能的频率提升,才会执行 App 或系统服务代码。
交互响应对 latency 的预算很窄。60Hz 屏幕一帧约 16.67ms,120Hz 屏幕一帧约 8.33ms。触摸事件从控制器中断进入输入子系统后,还要经过系统服务分发、App 主线程处理、渲染线程提交、GPU 执行和合成显示。任何一个阶段把 CPU 留在深 idle 过久、把 UI 线程排在后台任务之后、让中断风暴占用处理时间,都可能表现为触摸卡顿或掉帧。
timer latency 与 scheduling latency 的差别在排查中很有用。timer latency 指 timer 设定的过期点和实际触发点之间的偏差;scheduling latency 指 callback 已可运行后等待 CPU 的时间。前者常与 timer 精度、tickless idle、系统批处理、Doze 维护窗口有关;后者常与 run queue 长度、优先级、CPU idle exit、thermal throttling、中断负载和锁竞争有关。
贯穿材料中,前台下拉刷新要求立即响应。此时系统通常会提升交互线程优先级,让 CPU 从 idle 退出,并把网络请求、主线程状态更新、渲染提交纳入用户可见路径。后台每 15 分钟同步则属于低优先级工作,系统可以延迟、合并、降频执行。两个任务都可能使用 timer 或 callback,但调度策略完全不同,因为前者绑定输入和 frame deadline,后者绑定电源预算。
Android 上,前台 Activity、前台服务、Binder 回调、RenderThread、SurfaceFlinger、system_server 和 kernel scheduler 共同影响交互路径。App 主线程被后台计算占满时,即使输入事件及时到达,UI 仍会延迟;system_server 繁忙时,事件分发、窗口焦点、权限检查和服务回调也可能变慢;CPU 处于深 idle 时,退出延迟会叠加在第一帧响应上。
Apple 平台公开给开发者的主要调度语义是 QoS。QoS 把任务按用户交互、用户发起、utility、background 等重要性分类,系统据此调整调度、CPU 与 I/O 吞吐、timer latency 等行为。这个公开模型足以建立应用层判断:与当前 UI 直接相关的工作应声明更高 QoS;可延后同步、清理、预取应使用 background 或 discretionary 语义,让系统在能耗合适时处理。
调度延迟的判断顺序是:先确认事件是否真的到达系统,再确认目标线程是否可运行,然后看 run queue 和优先级,再看 CPU 是否从 idle 或低频状态返回,最后看是否存在温控、锁竞争或中断负载。这个顺序把“系统没触发 timer”“系统触发了但任务排队”“任务执行了但结果晚显示”三个问题分开。
32.4 Tick、Idle State 与 CPU 省电状态
Scheduler tick 是周期性打断 CPU 的时钟中断,用于进程时间统计、调度检查、profiling 和其他内核维护工作。周期 tick 简化了内核按时间推进状态的方式,但它会阻止 CPU 长时间保持 idle。手机系统为了续航,倾向在 CPU 空闲时减少 tick,并把下一次必要事件交给可编程 clock event device。
Tickless idle 的基本思路是:当某个 CPU 进入 idle,内核检查下一次 timer 或调度事件;如果下一次事件足够远,就关闭周期 tick 或把 tick 推迟到最近的必要事件。Linux NO_HZ_IDLE 就服务这个目标。公开文档说明,省略 idle CPU 上的调度 tick 对电池设备有明显价值,同时也会增加进出 idle 路径上的指令和时钟重编程开销。
Idle state 有层级差异。浅 idle 退出快、省电少;深 idle 退出慢、省电多。移动 SoC 还涉及 CPU cluster、L2/L3 cache、interconnect、display、modem、sensor hub 和 power domain。系统进入更深状态前,需要确认最近没有必须处理的 timer、没有未完成的 wake lock 或 assertion、没有驱动阻止 suspend、没有前台交互 deadline。
Power domain 是电源管理中的硬件边界。一个 CPU 核心可以 idle,但共享 cluster 仍保持供电;一个外设可以 runtime suspend,但所在电源域还受其他设备约束;display 可以关闭,但触摸、modem、RTC 和传感器 hub 保留唤醒能力。驱动和 PM core 需要维护这些依赖,使系统在关闭电源域前保存状态,在唤醒后按顺序恢复。
贯穿材料里的后台同步会影响 idle state。每 15 分钟精确唤醒一次,系统需要保留或重新编程 timer,并让 CPU、radio、网络栈和 App 进程参与执行。把同步任务交给系统批处理后,多个 App 的后台刷新可以合并到同一个维护窗口,CPU 一次退出 idle,radio 一次上电,随后系统回到低功耗状态。
Tick、idle 和 scheduling latency 的取舍集中在“第一响应”和“长期续航”之间。前台触摸和音视频播放要求低延迟,系统会减少深 idle 停留或提前做性能 hint;锁屏静置要求长 idle,系统会放宽 timer 精度、推迟后台任务、合并网络活动。用户看到的是“点亮快、滑动顺、后台更新稍晚、电池更耐用”;系统执行的是 tick、idle state 和唤醒预算的动态选择。
判断 tick/idle 问题时,先看设备是否处于前台交互、屏幕关闭、充电、低电量或温控状态;再看是否存在短周期 timer、持续 wake lock、常驻网络或传感器采样;接着看任务是否能批处理;最后看用户可见 deadline 是否允许系统进入更深 idle。这个顺序能解释“同样的 timer 前台准时、锁屏延迟、充电时又恢复”的现象。
32.5 Android WakeLock 与 Suspend Control
Android WakeLock 是应用或系统组件向 PowerManager 表达“这段工作需要设备保持某种唤醒状态”的机制。公开 API 中,App 需要 android.permission.WAKE_LOCK,通过 PowerManager.newWakeLock() 创建 WakeLock,调用 acquire() 持有,调用 release() 释放。持有 WakeLock 的代码应有明确工作边界和超时,因为它直接影响 CPU、屏幕或其他电源状态能否下降。
WakeLock 的系统路径可以按责任链理解。App 通过 framework API 提交请求;PowerManagerService 记录调用者身份、tag、WorkSource 和持有状态;电源管理策略综合屏幕、用户活动、充电、Doze、设备 idle、系统服务工作和内核 wakeup source;底层再通过 kernel wakeup source、suspend blocker、driver runtime PM 和设备电源状态控制系统能否 suspend。AOSP 具体实现会随 Android 版本和厂商修改变化,本章只使用公开 API 与 Android 电源管理的通用架构角色。
WakeLock 与 JobScheduler、AlarmManager 的关系是 Android 后台策略的核心。WakeLock 表示工作执行期间需要保持设备醒着;JobScheduler 表示工作可以在条件满足时由系统安排;AlarmManager 表示某个时间点需要系统处理。后台同步任务如果可延后,优先使用 JobScheduler 或 WorkManager 语义,让系统按网络、充电、idle、quota 和维护窗口调度。只有用户明确期望的准时提醒,才应进入 alarm 语义。
Doze 改变了普通后台 timer 的可执行性。Android 6.0 API level 23 及以上,设备锁屏、静置且未充电时进入 Doze 后,系统会限制网络和 CPU 密集服务,推迟 job、sync 和标准 alarm 到维护窗口。普通 WakeLock 在 Doze 限制下会被系统策略收束;允许 idle 期间触发的 alarm 也受到频率限制。这个边界解释了很多工程现象:App 已经调用 schedule,日志里也能看到任务登记,但锁屏后 callback 仍延后。
Suspend control 的判断重点是“谁阻止系统睡眠”。前台音乐播放、导航、正在上传的用户可见任务、系统下载、电话通话、闹钟响铃等路径通常具有明确用户结果;后台轮询、定期预取、埋点上传和缓存刷新通常应延后或批处理。系统会根据应用状态、权限、前台服务类型、通知可见性和电源策略决定执行窗口。
贯穿材料中,App 希望每 15 分钟同步。Android 下更稳的设计是:把普通刷新建模为可延后 job,把即时用户消息交给 FCM,把用户设定提醒交给 alarm,把执行中的短工作用有超时的 WakeLock 或系统提供的任务框架保护。这样责任边界清晰:App 描述工作意图,framework 管理 API 合约,system service 统一调度,kernel 和 driver 执行低功耗状态切换。
排查 Android 后台任务时,可以按六步看:任务入口是 alarm、job、push 还是前台服务;设备状态是否处于 Doze 或 App Standby;任务是否声明网络、充电、idle 等约束;是否存在用户可见通知;WakeLock 是否按路径释放;失败表现是 callback 未到、网络不可用、任务启动后被停止,还是通知展示晚到。每一步都对应不同责任主体。
32.6 Apple 平台的 Sleep / Wake 与电源状态管理
Apple 平台的 sleep/wake 和电源状态管理需要明确资料边界。公开文档能确认的内容包括:App 可通过 QoS 表达工作重要性,通过 timer tolerance 给系统合并执行空间,通过 Low Power Mode 状态调整活动,通过后台 URL session、BackgroundTasks、push notification 和特定 background mode 请求后台执行。iOS 内部 daemon、XNU driver、SEP、baseband 和私有电源策略的具体组合通常无法从公开文档完整验证。
Sleep / wake 在 App 视角表现为执行窗口的变化。前台 App 绑定用户交互和显示刷新,系统给予更高响应优先级;进入后台后,普通代码执行时间被收束,长期网络、定位、音频、蓝牙、VoIP、外设通信等需要对应的系统能力声明;设备锁定和低电量时,系统会进一步推迟 discretionary activity。这个模型让 App 把“想执行”转成“系统允许的后台能力”。
Apple Energy Guide 对 timer 的建议与移动 OS 电源策略一致。轮询式 timer 会阻止 CPU 进入或维持 idle;系统事件通知、dispatch source、后台 URL session 和推送通知更适合等待外部变化;必须使用 timer 时,应取消已无用的重复 timer,并设置 tolerance。tolerance 把多个 App 或系统任务聚合到相近时间点,减少唤醒次数。
QoS 是 Apple 公开调度模型中最适合解释 scheduling latency 的入口。系统根据工作对用户体验的影响调整调度、CPU 与 I/O 吞吐、timer latency。用户正在等待的 UI 更新应使用更高 QoS;预取、同步、清理、日志上传应使用低优先级或 discretionary 语义。QoS 选择错误会把后台工作放进交互路径,或把用户正在等待的工作放进可延后队列。
Low Power Mode 是公开可见的系统电源策略信号。iOS 9 及以上的 iPhone 可启用 Low Power Mode;公开文档说明系统可能降低 CPU/GPU 性能,暂停 discretionary 和后台活动,降低屏幕亮度,缩短自动锁定时间等。App 应监听电源状态变化,在该模式下降低动画、帧率、定位、同步和备份活动。这里的关键边界是:App 不能要求系统永久保持高性能状态;App 可以把工作强度和频率调低,使系统策略更容易完成续航目标。
Power assertion 在 Apple 语境里常见于 macOS 公开电源管理;iOS App 更多通过后台模式、任务框架、音频/定位/蓝牙等能力声明和系统服务获得有限执行窗口。跨 Apple 平台写作时应把这两类语义区分开:power assertion 表达“某段系统或进程工作期间暂缓睡眠”的请求,iOS background mode 表达“此类后台活动符合平台允许的用途”。二者都属于电源策略输入,但 API 表面和适用平台不同。
贯穿材料在 Apple 平台上的设计是:前台刷新使用合适 QoS 和异步网络;后台普通同步使用 BackgroundTasks 或后台 URL session 的 discretionary 语义;即时消息走 APNs;用户可见提醒走通知;低电量或锁屏时降低刷新频率。这个设计不依赖 Apple 私有实现细节,也能解释用户看到的结果:前台响应优先,后台同步可能延后,用户可见通知保留更高投递价值。
32.7 Timer、Wakeup、Power Policy 对后台任务的影响
后台任务是 timer、wakeup 和 power policy 的交汇点。App 关心“什么时候执行”,系统关心“这次执行是否值得唤醒设备、打开网络、提升频率、保留后台进程”。同一个 15 分钟同步请求,在前台、锁屏、充电、低电量、弱网、漫游、温控、用户很久未打开 App 等状态下,会得到不同执行窗口。
后台任务应先按用户结果分类。用户正在等待的结果,例如上传完成提示、导航位置、音频播放、通话、闹钟、即时通知,具有更强执行理由;缓存预热、推荐刷新、日志上传、统计同步、批量清理,适合延后、合并或只在充电/Wi-Fi 条件下运行。这个分类决定 API 入口,而 API 入口决定系统服务如何对它施加 quota、维护窗口和唤醒预算。
Android 与 Apple 的共同模型可以用同一组维度比较:
| 维度 | Android 典型路径 | Apple 典型路径 | 共同判断 |
|---|---|---|---|
| 普通后台同步 | JobScheduler / WorkManager | BackgroundTasks / background URL session | 交给系统批处理和条件调度 |
| 用户指定提醒 | AlarmManager / Notification | UserNotifications | 按用户可见时间语义处理 |
| 即时消息 | FCM | APNs | 使用平台推送聚合网络唤醒 |
| 长时间用户可见工作 | Foreground service 与通知 | 合法 background mode 或系统框架 | 让用户可见,并接受系统策略约束 |
| 低电量状态 | Battery Saver、Doze、App Standby | Low Power Mode | 降低后台频率、网络与计算强度 |
| 执行期间保活 | WakeLock、系统任务窗口 | 后台执行窗口、平台能力声明、部分平台 power assertion | 只保护有边界的工作区间 |
贯穿材料里的 15 分钟同步可以拆成三条路径。第一,前台用户打开页面时立即刷新,此路径受输入响应、QoS、线程调度和网络质量影响。第二,后台缓存刷新使用系统任务框架,允许延迟到维护窗口,此路径受 Doze、App Standby、Low Power Mode、充电和网络条件影响。第三,即时消息使用平台推送,只有用户可见通知或高优先级消息才获得更强唤醒价值。
Power policy 会把多个局部正确的请求重新排序。一个 App 认为“15 分钟一次很少”,一台手机上 200 个 App 都这样做,就会变成频繁唤醒、radio 反复上电、CPU 无法长 idle。系统服务因此把后台任务集中到队列、quota 和维护窗口中执行。这个设计牺牲了单个 App 的精确时间,换来整机续航、温控和后台公平性。
失败表现也要按路径分类。任务没有准时回调,可能是 timer 被批处理或 Doze 推迟;通知晚到,可能是消息优先级、推送通道、弱网或省电策略;任务启动后网络失败,可能是 idle 限制或 radio 未开放;任务运行一半停止,可能是后台执行窗口耗尽;前台卡顿,可能是后台任务抢占 CPU、I/O 或锁。把这些表现混成“系统限制后台”会丢失排查方向。
可复用判断顺序如下。先问用户可见结果是什么;再选 API 语义:alarm、job、push、foreground work、background transfer 或普通 async callback;接着看设备状态:屏幕、充电、电量、idle、网络、温控;然后定位执行边界:system service 是否准许,kernel 是否需要唤醒,driver 是否保持设备电源;最后看用户可见结果:准时展示、延后展示、维护窗口执行、取消、重试或静默失败。
本章的结论是:手机系统里的 timer 只回答“何时考虑执行”,wakeup source 回答“谁能叫醒设备”,scheduling latency 回答“醒来后多久拿到 CPU”,power policy 回答“这次执行是否符合整机预算”。后台任务、推送、触摸响应和掉帧都能放进这四个问题中定位责任边界。
最小自检任务
你负责排查一个移动 App 的后台刷新问题:产品希望每 15 分钟刷新一次首页数据;用户反馈锁屏一小时后再次打开 App,数据经常没有更新;同时即时消息通知大多数时间能收到,但有时延迟。请写出这条路径中 timer、wakeup source、scheduling latency 和 power policy 分别要检查什么,并分别说明 Android 与 Apple 平台上应优先使用的系统入口。
答案要点
timer 侧先判断“每 15 分钟”是否需要精确执行。首页缓存刷新通常没有用户指定时间语义,应进入可延后后台任务,而非高精度重复 timer。Android 可使用 JobScheduler 或 WorkManager;Apple 可使用 BackgroundTasks 或后台 URL session 的 discretionary 语义。若任务只是为了用户下次打开时更快,应允许系统合并到维护窗口。
wakeup source 侧先判断是否存在有资格唤醒设备的事件。首页刷新通常无需单独唤醒整机;即时消息应走平台推送。Android 使用 FCM,Apple 使用 APNs。用户可见且时间敏感的消息才应请求更高优先级;普通内容同步可等设备唤醒或维护窗口。
scheduling latency 侧要区分“系统没有准许任务运行”和“准许后线程排队”。锁屏一小时后的数据旧,优先看 Doze、App Standby、Low Power Mode、任务约束、网络状态和维护窗口;前台打开后刷新仍慢,再看主线程、QoS、run queue、网络回调、I/O 和渲染路径。
power policy 侧要把设备状态放入判断:锁屏、未充电、低电量、弱网、温控和用户久未打开 App 都会降低后台执行机会。Android 上检查 Doze、App Standby、Battery Saver、alarm/job 约束、WakeLock 生命周期和通知可见性;Apple 上检查 Low Power Mode、background mode 合法性、BackgroundTasks 调度、后台 URL session、APNs 优先级和 App 被用户强制结束后的行为边界。
最终设计应拆成三条路径:前台打开页面时即时刷新;后台缓存刷新交给系统批处理;即时消息走平台推送并只在用户可见场景使用高优先级。这样能解释数据延后、通知延迟和前台响应三个现象,也能把责任边界分配给 App、framework、system service、kernel/driver 和硬件唤醒源。
本章知识点总结
- Timer 语义:Timer 表示未来时间条件,系统还会根据精度、用户可见性和电源状态决定实际执行窗口。
- 时间分类:Wall clock 适合日历和用户提醒,monotonic time 适合超时、重试、动画和耗时统计。
- 内核计时:Clock source 提供当前时间读数,clock event device 负责安排下一次计时中断。
- 高精度成本:高精度短周期 timer 会增加 CPU 唤醒次数,并压缩设备停留在深 idle 的时间。
- Alarm 边界:Alarm 适合具有明确时间语义的提醒或系统事件,普通后台同步更适合任务调度入口。
- 唤醒来源:Wakeup source 决定睡眠中的设备能被谁叫醒,timer 到期本身仍需具备可唤醒路径。
- 中断边界:运行时可产生中断的设备,还需要额外唤醒配置才能在 suspend 中唤醒整机。
- 延迟拆分:Timer latency 描述过期点到触发点的偏差,scheduling latency 描述可运行线程等待 CPU 的时间。
- 交互预算:触摸和帧输出受毫秒级 deadline 约束,后台同步可使用更宽松的调度窗口。
- Tickless idle:减少 idle CPU 上的 scheduler tick 能提升续航,但会带来进出 idle 和重编程时钟的开销。
- WakeLock 责任:Android WakeLock 表达工作执行期间保持唤醒状态的请求,持有和释放必须对应明确工作边界。
- Doze 策略:Android Doze 会推迟普通 alarm、job、sync 和网络访问,把后台工作集中到维护窗口。
- Apple 边界:Apple 平台公开可用的判断入口是 timer tolerance、QoS、Low Power Mode、后台任务框架和推送通道。
- 后台分类:用户可见工作、普通缓存刷新、即时消息和用户指定提醒应使用不同 API 入口。
- 排查顺序:先看用户结果,再看 API 语义,然后看设备状态、系统准许、内核唤醒和最终可见表现。