功耗与电量优化
# 功耗与电量优化
📊 学习成本预估 | 难度:⭐⭐⭐⭐(4/5)| 阅读:约 45 分钟 | 实操:3 小时 🔗 前置阅读:卷二·06, 卷四·02 | ➡️ 后续延伸:卷五·04
# 目录介绍
- 01.阅读说明
- 02.贯穿案例
- 03.功耗本质定义
- 04.功耗数学原理
- 05.度量与采集
- 06.归因决策树
- 07.屏幕全链路 ⭐
- 08.CPU 全链路 ⭐
- 09.通信全链路 ⭐
- 10.定位与传感器 ⭐
- 11.后台调度全链路 ⭐
- 12.跨端功耗对照
- 13.治理一层少 ⭐
- 14.治理二层批 ⭐
- 15.治理三层让 ⭐
- 16.治理四层轻 ⭐
- 17.求证实验 ⭐
- 18.实战案例
- 19.防劣化体系
- 20.跨平台速查
- 21.总结与延伸
# 01.阅读说明
- 本文卷归属:卷四 · 业务专项 · 第 5 篇
- 本文目标层级:L3 专家
- 适用平台:Android / iOS / Web
- 前置阅读:
卷二·06 GPU 与渲染观测(屏幕功耗根源)卷四·02 网络性能分析优化(通信功耗根源)
- 本文核心命题:
功耗是性能领域的"长尾杀手"——单次开销很小,但累积放大后能让用户的电池一天少撑 2 小时。 省电 = 让硬件多睡觉——核心不是"做得快",而是"做得少 + 做得集中 + 选低耗硬件"。 功耗优化的反直觉是"它和性能优化经常相反":多线程加速更耗电、预加载更耗电、高刷新率更耗电。
# 02.贯穿案例
本案例贯穿全文:§03 看懂功耗物理本质、§04 用数学模型量化、§07-§11 拆解各模块原理、§17 用实验复盘、§13-§16 给出分层闭环。
# 2.1 案例背景
某出行类导航 App V6.0 上线"全程实时车辆追踪"功能(产品要求"用户开车 1 小时也能稳定定位"),用户反馈崩塌:
- 用 1 小时手机耗电 35%(之前 8%)。
- 手机持续发烫,部分机型触发降频,定位精度反而下降。
- 应用商店一星差评激增:"开个导航手机就废了"。
- 100 万 DAU 中投诉 12,000+/日。
- 与百度地图/高德对比测试:竞品同场景 1 小时耗电 10-12%,功耗 3 倍。
研发组初步反应:"导航本来就费电,这是物理限制。"——这是典型的"物理背锅"。
# 2.2 经验派的 3 周折腾(典型反面教材)
| 周次 | 动作 | 结果 |
|---|---|---|
| 第 1 周 | 把屏幕亮度降低到自动(怕屏幕耗电) | 用户看不清,体验差;耗电降到 32% |
| 第 2 周 | 关闭语音播报(怕扬声器耗电) | 用户怒了:导航不语音播报?耗电 30% |
| 第 3 周 | 降低地图刷新率到 30fps(怕 GPU 耗电) | 地图卡顿,耗电 28%(仅微降) |
复盘:三周折腾错在"四处找耗电源",但没碰到真正根因——GPS 高精度定位 + 网络心跳 + 后台多任务才是 80% 的耗电源。屏幕和 GPU 都不是主战场。这正对应 §04.3 功耗占比定律 ——必须先量化各模块占比再下手。
# 2.3 方法派的 5 天闭环
Day 1(§05 物理台 + 系统统计联合诊断):
- Monsoon 物理台测试:发现 GPS 持续高精度占电 38%、网络心跳 22%、屏幕 25%、其他 15%。
- batterystats:定位服务 1 小时唤醒 7200 次(2s 一次)。
→ GPS 高频高精度是元凶,竞品大多用"传感器融合 + 低频 GPS"。
Day 2(§06 决策树):
- 第 1 层(少):评估每个后台任务必要性,砍掉 60% 非核心。
- 第 2 层(批):定位从 2s/次 改为 5s/次 + 加速度传感器融合(移动慢时延长到 10s)。
- 第 3 层(让):把"地图预加载"改用 WorkManager 在 WiFi + 充电时执行。
- 第 4 层(轻):定位策略升级——直行时低频 GPS、转弯时高频,省 50%。
Day 5(上线 + 灰度验证)。
# 2.4 上线效果
| 指标 | 经验派 3 周后 | 方法派 5 天后 | 竞品 |
|---|---|---|---|
| 1 小时导航耗电 | 28% | 11% | 10-12% |
| GPS 唤醒次数/h | 7200 | 720 | - |
| 后台 CPU 占比 | 8% | 1.2% | - |
| 应用商店"耗电"差评 | 持续 | 下降 90% | - |
| 定位精度(直行场景) | 5m | 8m(无感知差) | 6m |
核心洞察:功耗优化 80% 收益来自定位 + 网络两个模块,屏幕和 GPU 是小头。经验派错在没分清主次——一开始就该用物理台精确归因。功耗治理 = 让硬件多睡觉,不是降低单次质量。
# 2.5 案例串联全文
- §03 功耗物理本质 ▶▶ 通信和屏幕是大头,但定位是"通信 + 传感器"双高耗。
- §04 数学原理 ▶▶ tail time 解释了"高频心跳为什么是电池杀手"。
- §06 决策树 ▶▶ 前台分支精准命中定位高耗。
- §09 RRC 原理 ▶▶ 解释为什么 2s 一次心跳让通信 24h 高功耗保持。
- §10 GPS 原理 ▶▶ 解释 GPS 冷启动 30s 的物理代价 + 传感器融合的工程妙处。
- §17 求证实验 ▶▶ §17.2 + §17.3 都在案例中变现。
- §13-§16 分层策略 ▶▶ "少→批→让→轻"四层正是案例落地路径。
# 03.功耗本质定义
# 3.1 电池物理本质
功耗 = 能量 / 时间 = 电池在单位时间内输出的电荷。
电池容量用 mAh(毫安·时)衡量——一块 4000mAh 电池在 1mA 持续放电下能撑 4000 小时,10mA 下 400 小时,100mA 下 40 小时。这是不可商量的物理约束。
三个不可商量的物理事实:
事实一:电池是"消耗品"而非"补给品"——锂电池循环 500-800 次后容量衰减到 80%。省电不只是为了"今天多撑两小时",更是"两年后这块电池还能用"。
事实二:所有计算最终变成热量——CPU 跑 1 焦耳 ≈ 释放 1 焦耳热。手机散热只有自然对流(无风扇),过热触发降频反而拖慢应用。功耗和散热是同一枚硬币。
事实三:硬件不存在"半睡眠"——硬件状态只有"工作"和"休眠"两种本质态。所谓"省电"只能让硬件在休眠态待更久,不能"少干活的工作态"。
探索性思考:为什么"提速 2 倍"不等于"能耗降一半"? 直觉认为"早干完早休息",但 CPU 频率和功耗的关系是 P ∝ V²·f——频率翻倍意味着电压也得提,功耗增加 4-8 倍。race-to-idle 策略(高频快跑然后休眠)只在"休眠时间足够长"时才赢。后台 30s 心跳这种"打一枪就睡"场景,race-to-idle 反而费电——见 §08.3。
# 3.2 功耗的硬件构成
手机功耗主要由四部分组成:
| 模块 | 占比 | 关键 |
|---|---|---|
| 屏幕 | 30-50% | 亮度、刷新率、色深、屏幕类型(OLED/LCD) |
| CPU/SoC | 20-30% | 频率、核数、任务密度 |
| 通信 | 15-25% | WiFi/4G/5G/GPS 唤醒频次与持续 |
| 其他 | 10% | 传感器、振动、扬声器 |
关键认知:屏幕和通信是最大头,CPU 反而不是。这与"性能优化主战场是 CPU"形成鲜明对比。
▶▶ 回扣 §02 案例:导航场景下"屏幕 25% + GPS 38% + 网络心跳 22%"——定位类应用是反常态:通信+定位 60%+,远超普通 App。功耗优化必须按场景分析占比,不能套通用模型。
# 3.3 三态模型(不可商量)
所有耗电硬件本质上都遵循"工作 → 高功耗保持(tail time)→ 休眠"三态:
┌──────────────────────────────────────────────────────┐
│ A. 工作态(active):硬件全功率,处理实际任务 │
│ 示例:CPU 运算、GPS 解算、4G 收发数据 │
├──────────────────────────────────────────────────────┤
│ B. 高功耗保持(tail time):任务结束但硬件未休眠 │
│ 示例:4G 任务结束后保持连接 5-10s 等可能下次 │
│ 物理本质:休眠唤醒成本太高,留个余地 │
├──────────────────────────────────────────────────────┤
│ C. 休眠态(idle):硬件最低功率,只保留唤醒能力 │
│ 示例:CPU 进入 deep sleep,4G 进入 IDLE │
└──────────────────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
三态模型的两个工程含义:
- 真正省电的是 C 态——A/B 态都在耗电。让硬件尽快从 A→C,跳过 B 是不可能的(系统统一管理)。
- B 态是"批量化"的物理基础——既然必须保持 5-10s,就让这段时间多干点活,不要刚回 C 态又起来。这是 §14 批量化治理 的根本原理。
# 3.4 用户感知层定义
用户对功耗的感知不是线性的,而是阶梯式:
| 1 小时耗电 | 用户感知 | 行为 |
|---|---|---|
| < 5% | 无感 | 不在意 |
| 5-10% | 轻度感知 | 偶尔看一眼电量 |
| 10-20% | 明显费电 | 开始抱怨"这 App 费电" |
| > 20% | 强烈不满 | 卸载 / 一星评分 |
关键阈值:前台 10%/h 是用户耐心的临界值——这是 §02 案例 中竞品 11% 和故障版本 35% 体感差异 3-4 倍的根因。
探索性思考:为什么用户感知不是线性而是阶梯? 因为用户对电量的判断不是"百分比"而是"还能撑多久"——电量从 100% → 90% 没感觉,从 30% → 20% 焦虑——绝对值差只是 10%,但"剩余使用时长"差几倍。电量感知是非线性的,所以耗电曲线必须扁平。这也是为什么"掉电曲线斜率"比"绝对耗电值"更重要的指标设计——见 §05.2 系统统计的局限。
# 3.5 8 个反直觉问题
带着这些问题阅读:
- 为什么 App 在前台用得多反而比后台耗电少(按比例)?
- 屏幕亮 1 秒和 CPU 跑 1 秒,哪个更耗电?
- WiFi 和 4G 哪个更省电?
- 为什么"心跳间隔越长越省电"不一定成立?
- AlarmManager 的"精确"和"不精确"差几十倍功耗?
- 后台定位为什么是电量黑洞?
- 为什么 push 推送比 App 自轮询省电?
- 暗黑模式真的能省电吗?
# 04.功耗数学原理
本章把功耗从"经验数字"还原到第一性原理,回答四个核心问题:功耗如何计算 / tail time 怎么把成本放大 / 占比定律为何决定优化优先级 / race-to-idle 何时成立。
# 4.1 功耗基本公式
能耗 E = ∫ P(t) dt (瓦时 / 焦耳)
功率 P = U × I (瓦 = 电压 × 电流)
电池消耗 = E / U_battery (mAh = 能耗 / 电池电压)
2
3
三个推论:
- 功耗是积分量,不是瞬时量——P=10W 跑 1s 和 P=1W 跑 10s 等价。减少时长和降低瞬时都是手段,但前者通常更有效。
- 不同硬件电压不同——CPU 1.0V、屏幕 4.0V、通信芯片 3.3V,同样电流下屏幕功耗最高。这是为什么屏幕占 30-50% 的物理根源。
- 电池是 mAh 而非 mWh——所以厂商常用"电流 mA"作为功耗代理指标。但严格说,应该用 mW 才对——这是后面 §17 实验 数据需要注意的细节。
# 4.2 tail time 数学模型
一次任务实际成本 = T_active × P_active + T_tail × P_tail
│ │ │ │
任务时长 工作功率 保持时长 保持功率
2
3
举例:4G 一次小数据发送 100ms,但 tail time 5s,实际成本是发送的 50 倍。
这解释了"批量化是省电的元方法":
方案 A:100 个包,1 秒发完
总成本 = 1s × P_active + 5s × P_tail = 6 个单位
方案 B:100 个包,分散在 100 秒
总成本 = 100 × (10ms × P_active + 5s × P_tail) ≈ 500 个单位
差距:B 比 A 耗电 80 倍以上
2
3
4
5
6
7
这个数学模型的一个非显然推论:间隔 < tail time 的请求等同于"持续在线"——比如 4G tail 5s,每 3s 一次心跳的应用,相当于 24h 通信芯片永不休眠。这就是为什么 §17.1 实验 中 30s 心跳功耗是 5min 心跳的 3 倍。
探索性思考:为什么 tail time 是硬件设计的"必要恶"而非可优化点? tail time 不是"工程师懒",而是物理约束——通信芯片从 IDLE → CONNECTED 需要 1-3s 的握手(功耗很高)。如果应用 1s 后又要发包,重新建连比保持高耗状态还费电。所以芯片必须"等一会再睡"。应用层无法消除 tail time,只能配合它做批量。
# 4.3 功耗占比定律
总功耗 = Σ (各模块占比 × 各模块绝对功耗)
优化优先级 = 占比 × 可优化空间——这是 §02 案例 经验派失败的根因:
| 模块 | 占比 | 可压缩空间 | 优先级 |
|---|---|---|---|
| GPS(导航场景) | 38% | 50%(融合) | 极高 |
| 通信心跳 | 22% | 80%(批量) | 极高 |
| 屏幕 | 25% | 5-10%(用户已亮) | 低 |
| GPU | 5% | 20% | 极低 |
经验派优先动屏幕/GPU——动了最低优先级模块。方法派先用物理台量化占比,直击 GPS+心跳——动了最高优先级模块。
通用工程规则:先用 §05 度量 拿到占比,再决定动谁。不量化就动手等于赌博。
# 4.4 race-to-idle 的成立条件
直觉:"早干完早睡 → 省电"。但要看 CPU 频率—功耗曲线 P ∝ V²·f:
| 频率 | 电压 | 功耗 | 单位时间能效 |
|---|---|---|---|
| 1 GHz | 0.7V | 1×(基准) | 高 |
| 2 GHz | 0.9V | 3.3× | 中 |
| 3 GHz | 1.1V | 7.7× | 低 |
race-to-idle 成立的条件是:节省的休眠时间 × 休眠功耗收益 > 高频额外功耗。
实践规则:
- 任务长(>500ms) → race-to-idle 划算(节省休眠值钱)。
- 任务短(<100ms) → 中频更省(高频拉起就关,划不来)。
- 后台周期任务(每 30s 跑一次) → 低频最省(任务多到等不来"长休眠")。
探索性思考:为什么调度器不"完美"决策? 因为调度器不知道任务的"未来"——它能看到当前 30ms 任务,但不知道 5s 后是否再来一波。所以现代 Android 调度器(EAS)用"启发式"——观察过去预测未来,但总会有滞后。这就是为什么开发者要用
JobScheduler显式告诉系统"这是个可批量任务"——让系统调度器决策更准。
# 4.5 跨平台同构原理
所有平台的功耗本质都遵循相同公式:
E = Σ_modules ∫ P_module(t) dt
优化 = min(E) s.t. 业务需求约束
2
跨平台术语对照
| 通用术语 | Android | iOS | Web |
|---|---|---|---|
| 工作态 | running / active | active | active tab |
| 高功耗保持 | RRC connected | radio active | network keepalive |
| 休眠态 | Doze / deep sleep | suspended | bfcache / background throttle |
| 调度器 | JobScheduler / WorkManager | BGTaskScheduler | requestIdleCallback |
| 系统强省电 | 厂商 ROM 后台清理 | Low Power Mode | tab discarding |
同构本质:所有移动平台都遵循"硬件功耗模型 + 唤醒时长 × 功率"框架。差别只在 API 名字和具体阈值。
# 05.度量与采集
# 5.1 三类捕获方案
所有平台的功耗度量方案,本质上只有 3 类:
① 物理功耗台(地面真值)
② 系统电量统计(估算 + 易得)
③ 业务事件归因(线上可追踪)
2
3
① 物理功耗台——直接接入电池电流监测设备(Monsoon/Power Profiler)。物理本质:硬件级测量电流,毫安级精度,地面真值。局限根源:实验室设备,不可线上化。适用场景:性能基线、新功能上线前的功耗实验、归因测试。
② 系统电量统计——读取 batterystats(Android)或 IOPMrootDomain(iOS)系统级耗电统计。物理本质:基于硬件功耗模型,按"唤醒时长 × 模块功率"估算每个 App 耗电。局限根源:只是估算(误差 10-30%),不是真实测量。适用场景:相对对比、长期趋势、A/B 实验。
③ 业务事件归因——埋点记录所有"高耗能事件"(CPU 高、网络发送、屏幕唤醒)。物理本质:以"工程归因"换取"线上可观测"。每次唤醒/发送/定位带时长 + 调用栈。局限根源:只能归因到事件,不能直接换算电量。适用场景:线上根因分析、用户级问题诊断。
# 5.2 三方案差异矩阵
| 维度 | ① 物理台 | ② 系统统计 | ③ 事件归因 |
|---|---|---|---|
| 精度 | 极高(毫安级) | 中(估算) | 间接(事件级) |
| 上线可用 | ❌ | ✅ | ✅ |
| 归因能力 | 强 | 弱 | 强 |
| 成本 | 高(设备/人力) | 0 | 中(埋点开发) |
| 适用阶段 | 研发期实验 | 灰度/线上对比 | 线上根因 |
方案的"组合定律":①+②+③ 必须组合——①给真值,②给上线趋势,③给具体根因。
# 5.3 跨平台采集对照表
| 平台 | 物理台 | 系统统计 | 事件归因 |
|---|---|---|---|
| Android | Monsoon Power Monitor | dumpsys batterystats / Battery Historian | WakeLock / JobScheduler / WorkManager 埋点 |
| iOS | 同上(接电池) | MetricKit MXEnergyMetricPayload | BackgroundTask / Location 埋点 |
| Web | N/A | navigator.getBattery()(已废弃) | Performance API + 后台事件 |
同构本质:所有移动平台都提供"系统级功耗模型 + 应用级事件钩子"两套 API。Web 是例外——浏览器为隐私限制了功耗 API,只能从"行为"推算"功耗"。
# 5.4 数据可信度评估
| 数据 | 可信度 | 偏差来源 |
|---|---|---|
| 物理台电流 | 极高 | 极端温度环境略有差异 |
| batterystats 各 App 占比 | 中 | 估算模型,误差 10-30% |
| MetricKit 能耗指标 | 中 | 设备聚合,延迟 24h |
| 唤醒次数/WakeLock 时长 | 高 | 直接 OS 计数 |
| 用户主观反馈 | 低 | 受心理预期影响大 |
探索性思考:为什么 Android batterystats 的应用级估算"够用但不准"? 因为它的核心计算是 耗电 = Σ (硬件唤醒时长 × 硬件标称功率)——但硬件功率是"机型出厂时的固定值",不会随实际负载变。比如 CPU 标定 800mW,但你实际跑的是单核 30% 占用,实际耗电 ≈ 60mW。所以 batterystats 在重负载下偏低、轻负载下偏高。对比和趋势可信,绝对值不可信。
# 06.归因决策树
# 6.1 功耗问题二分决策树
耗电高
├─ 前台还是后台?
│ ├─ 前台 → 屏幕/CPU/网络密集(看哪类,对应 §07/§08/§09)
│ └─ 后台 → 唤醒次数过多(最常见)
│ ├─ 网络心跳? → §09 RRC 状态机
│ ├─ 定位服务? → §10 GPS 原理
│ ├─ Job/Alarm 频繁触发? → §11 后台调度
│ └─ 第三方 SDK? → §13 治理一层
└─ 是个别用户还是全量?
├─ 个别 → 设备 / 网络环境特异(如低信号触发持续重试)
└─ 全量 → 通用问题,深入排查 §6.2
2
3
4
5
6
7
8
9
10
11
▶▶ 回扣 §02 案例:导航 App 走"前台 + 全量"分支,进一步定位"定位服务"分支。经验派 3 周折腾错在没走决策树,凭直觉找屏幕/GPU/扬声器,全部偏离主战场。
# 6.2 后台耗电四大元凶
| 元凶 | 表现 | 物理根因 | 治理章节 |
|---|---|---|---|
| WakeLock 滥用 | 长时间持有 PARTIAL_WAKE_LOCK | CPU 无法进入 deep sleep | §13.3 |
| 网络心跳过频 | < tail time 间隔的请求 | 通信芯片永不休眠(§04.2) | §14.1 |
| 定位精度过高 | 后台用 GPS 高精度定位 | GPS 工作功率 30-50mA | §16.1 |
| AlarmManager 精确触发 | setExact 频繁 | CPU 无法批量唤醒 | §14.3 |
# 6.3 唤醒次数 vs 唤醒时长
后台耗电核心指标:
- 唤醒次数(次/小时):影响通信 tail time,每次唤醒成本 ≈ 5-10s 通信高耗
- 唤醒时长占比(%):影响 CPU 高频时段
- 联合指标:tail time 总占比 = 唤醒次数 × 5s / 总时间
后台 1 小时唤醒次数 N
tail time 5s
通信高耗占比 N × 5 / 3600
= N × 0.14%
N=10 → 1.4%(合理)
N=60 → 8.3%(异常高,几乎一直亮着)
N=200 → > 100%(永远高耗) ← 这就是高频心跳的本质
2
3
4
5
6
7
8
探索性思考:为什么"唤醒次数"比"CPU 占用率"更能预测耗电? 因为现代 CPU 闲时进 deep sleep 极快(<10ms),但通信芯片 tail time 5-10s 是数量级差距。一次"轻量级"网络请求(CPU 占用 1ms)可能让通信芯片亮 10s——这 10s 通信功耗是 1ms CPU 的 100-1000 倍。所以"减少唤醒次数 > 减少 CPU 占用"是后台优化的铁律。
# 6.4 长尾耗电归因
少数用户的耗电异常往往是:
- 低信号环境:信号差时通信芯片自动放大功率重试,实际功耗能翻 5-10 倍。
- 第三方 SDK 失控:广告/推送 SDK 重试不收敛。
- 设备老化:电池循环 500+ 次后容量衰减,相同耗电体感"更费电"。
- 罕见组合:某机型 + 某 ROM + 某网络环境组合触发的 bug。
长尾治理思路:
- 设定功耗 SLO(如后台每小时 ≤ 0.5%)+ 极端环境降级(信号差时主动降级心跳频率)。
- 对老化电池自适应——读取
BatteryManager.BATTERY_PROPERTY_CAPACITY健康度后调整。
# 07.屏幕全链路 ⭐
本章把屏幕功耗从"光子的物理"一路拆到"应用层 API",回答四个核心问题:屏幕为何占 30-50% / OLED 和 LCD 物理差异 / 高刷为何耗电 / 应用能干预多少。
# 7.1 屏幕功耗的物理根源
屏幕功耗 = 背光功耗 + 像素发光功耗 + 驱动电路 + GPU/Display Engine。
不同屏幕类型的物理机制完全不同:
| 屏幕类型 | 工作原理 | 暗黑模式效果 | 占比变化 |
|---|---|---|---|
| LCD | 液晶 + 全幅背光 | 无效(背光始终全亮) | 1.0× |
| OLED | 每像素自发光 | 极有效(黑色像素不耗电) | 0.4× |
| AMOLED | OLED + 主动驱动 | 极有效 | 0.4× |
| MicroLED | 无机自发光 | 有效(亮度高更耗) | 0.5× |
关键认知:OLED 暗黑模式真省电(-58%),LCD 暗黑模式无效。这不是"软件功能开关",是物理事实。
探索性思考:为什么 LCD 不会被淘汰? LCD 在强光下亮度上限更高(户外可读性好)、寿命更长(OLED 像素长期亮同色会"烧屏")、成本低(中低端机标配)。所以屏幕类型选择是用户场景的决策,不是"哪种更好"。应用层不能假设所有用户都是 OLED——这是 §17.4 实验 "暗黑推荐前先检测屏幕类型"的前提。
# 7.2 亮度的非线性功耗
屏幕亮度和功耗不是线性关系:
亮度 0% → 仅驱动电路 (5%)
亮度 25% → +背光 (40%)
亮度 50% → 70%
亮度 75% → 88%
亮度 100% → 100%
2
3
4
5
两个工程含义:
- 从 100% 降到 50% 省 30%,但从 50% 降到 25% 仅省 30%——降亮度的边际收益递减。
- 应用强制降亮度损害用户体验且收益小——这是 §02 案例 经验派第 1 周失败的根因。
# 7.3 高刷新率的功耗代价
| 刷新率 | 屏幕功耗倍数 | GPU 功耗倍数 | 用户感知 |
|---|---|---|---|
| 60Hz | 1.0× | 1.0× | 标准流畅 |
| 90Hz | 1.3× | 1.5× | 滚动更顺 |
| 120Hz | 1.6× | 2.0× | 游戏更顺 |
反直觉事实:高刷不是"屏幕变快",而是"屏幕和 GPU 都加倍工作"。这就是为什么 Android 12+ 引入 LTPO(Low-Temperature PolyOxide)—— 静态画面降到 1Hz、滑动时升到 120Hz。
应用层的关键 API:
// Android 11+:声明本 Activity 偏好刷新率(让系统决策)
val params = window.attributes
params.preferredRefreshRate = 60f // 文本阅读场景
window.attributes = params
2
3
4
# 7.4 应用层可干预点
应用对屏幕功耗的"可干预"非常有限:
| 干预点 | 收益 | 风险 |
|---|---|---|
| 暗黑模式(OLED) | -58% | LCD 无效,需检测 |
| 降低 Activity 刷新率 | -30%(120→60) | 滚动体感打折 |
| 减少 overdraw | GPU -10% | 改造成本高 |
| 缩短自动息屏时间 | 长期省电 | 用户体验下降 |
关键认知:屏幕功耗的 80% 由用户自己控制(亮度、息屏时间)——应用只能在 20% 边缘做优化。这就是为什么 §02 案例 经验派降屏幕亮度走错——屏幕不是应用层的主战场。
探索性思考:为什么"暗黑模式"成了功耗优化的神话? 因为它符合 OLED 的物理特性——像素不亮就不耗电,几乎是唯一让屏幕功耗结构性下降的手段。但媒体宣传时往往忽略 LCD 的局限,导致用户在 LCD 设备上"暗黑了但没省电"。应用层做暗黑推荐时必须检测屏幕类型:见 §16.2。
# 08.CPU 全链路 ⭐
本章把 CPU 功耗从"晶体管动态功耗"一路拆到"应用层任务调度",回答:为什么 P ∝ V²·f / big.LITTLE 怎么省电 / Doze 怎么强制 race-to-idle。
# 8.1 CPU 功耗的物理根源
CPU 功耗 P = P_dynamic + P_static
= α × C × V² × f + I_leak × V
│ │ │ │
活跃因子 电容 电压 频率
2
3
4
三个推论:
- 频率翻倍 → 功耗翻 4-8 倍(因为电压也得提)。这就是 §04.4 race-to-idle 不总成立的根因。
- 静态功耗(漏电)随温度指数增长——手机过热时 leakage 暴涨,再叠加降频。
- C(电容)由制程决定——5nm 比 14nm 省电的根因。应用层无法改变。
# 8.2 big.LITTLE 架构原理
现代手机 SoC 都是 big + LITTLE 混合架构:
| 核心类型 | 频率 | 功耗 | 适用任务 |
|---|---|---|---|
| LITTLE | 1.5-2 GHz | 1× | 后台同步、心跳、轻量 UI |
| big | 2.5-3 GHz | 4× | 启动、动画、计算密集 |
| Prime(旗舰) | 3-3.3 GHz | 8× | 游戏、AI 推理 |
调度器决策原理:Linux EAS(Energy Aware Scheduler)基于"任务负载预测"决定放哪种核——负载越高的任务越倾向 big 核。这就是为什么"任务集中"反而省电——让 LITTLE 在 80% 时间空闲,而不是把负载平摊到 4 个 LITTLE 都半忙。
探索性思考:为什么"多线程不一定省电"? 把任务从 1 个核拆到 4 个核可能加快 4×,但功耗却不一定降 4×——4 个核都进 active 态、每个核都需要 cache/TLB 唤醒、跨核同步成本高。对小任务(< 100ms)单核串行往往更省。这就是为什么后台心跳应该单线程跑完——见 §14.1。
# 8.3 DVFS 与 big.LITTLE 原理
DVFS(Dynamic Voltage and Frequency Scaling):CPU 根据负载动态调频——闲时降频降压、忙时升频升压。
[应用任务] → 调度器评估负载 → governor 决定频率
│
┌──────────────┴──────────────┐
│ │
schedutil(5.0+) interactive(旧)
基于 EAS 模型 基于负载阈值
考虑能耗最优 简单快速
2
3
4
5
6
7
应用层影响调度的方式:
// 1. 提示线程优先级(影响调度但不强制核选择)
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
// 2. 协程的调度上下文(Android 13+ 影响核选择)
withContext(Dispatchers.IO) { /* 倾向 LITTLE */ }
withContext(Dispatchers.Default) { /* 计算密集,可能上 big */ }
// 3. 直接绑核(NDK,慎用)
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
2
3
4
5
6
7
8
9
绑核的危险:直接绑 big 核虽然快,但违反系统调度策略,可能被 ROM 限流甚至杀进程。正常应用绝不用 sched_setaffinity。
# 8.4 Doze 与 App Standby 原理
Android 6+ 引入两个强制省电机制:
Doze 模式(Deep Idle):
屏幕灭 + 不充电 + 静止 1 小时
↓
进入 Doze——所有网络/Job/Alarm 暂停
↓
维护窗口(每 30/60/180 分钟)短暂放行批量任务
↓
屏幕亮 / 移动 → 退出 Doze
2
3
4
5
6
7
App Standby(应用休眠):
未使用 N 天 → 进入 Standby Bucket
↓
限制后台 Job/Alarm/网络
↓
用户打开 → 退回 active
2
3
4
5
这两套机制的工程意图:强制应用批量化。系统不再相信应用"会自觉省电",直接物理层切断后台任务。
App 配合姿势:
| 错误做法 | 正确做法 | 原因 |
|---|---|---|
| 用 setExactAndAllowWhileIdle 绕过 Doze | 用 setInexactRepeating 让系统批量 | 绕过会被系统降权 |
| 申请 ignore battery optimization 白名单 | 不申请,靠 push 触达 | 白名单是用户体验杀手 |
| 自实现长连接 | 用 FCM/厂商推送 | 系统级长连接共享心跳 |
探索性思考:为什么 Google 不让应用"自由后台运行"? 因为Android 早期生态是"野蛮生长"——大量应用滥用后台导致用户电池一天都撑不住。Doze 是 Google 的"行政干预"——既然应用不自律,OS 就强制省电。这反映了一个工程哲学:在多方协作但责任分散的系统里,系统层往往要做"家长"。这也是为什么 iOS 从一开始就"严格管控后台"——见 §11.2。
# 09.通信全链路 ⭐
本章把通信功耗从"无线电波物理"一路拆到"应用层 API",回答:RRC 状态机如何决定 tail time / 为什么"小数据用 5G 反而费电" / 长连接应该多长。
# 9.1 通信功耗的物理根源
无线通信功耗 = PA(功率放大器)+ 调制解调 + 协议栈。
| 网络类型 | 待机 | 收发数据 | tail time | 物理特性 |
|---|---|---|---|---|
| WiFi | 5mA | 60-100mA | 0.5-1s | 短距离,低功耗 |
| 4G | 15mA | 80-150mA | 5-10s | RRC 状态机复杂 |
| 5G | 25mA | 90-200mA | 10-20s | 毫米波天线阵列 + 更复杂状态机 |
| GPS 接收 | 0mA | 30-50mA | 0(持续) | 低噪声放大器持续工作 |
关键差异:4G/5G 的 tail time 比 WiFi 长 10 倍——这是它们"高功耗"的最大根源。
# 9.2 RRC 状态机原理
4G/5G 通信芯片有完整的 RRC(Radio Resource Control)状态机:
RRC_IDLE(休眠) ← 待机,仅响应寻呼
↑ ↓
│ │ 收到数据 / 应用要发数据
│ ↓
RRC_CONNECTED(活跃) ← 全功率,可收发
↑
│ tail timer 倒计时
│ (5-10s 不发数据后切回)
│
RRC_INACTIVE(5G 新增) ← 中间态,更省电
2
3
4
5
6
7
8
9
10
关键事实:
- 从 IDLE → CONNECTED 需要 1-3s 握手——握手过程功耗很高。
- CONNECTED 状态保持 5-10s(tail time)——这段时间持续高耗。
- 应用任意一次发包都会触发完整路径——所以"批量"才能摊薄成本。
数学例子:心跳 30s 一次
每次心跳实际占用通信芯片 = 1s 握手 + 100ms 发送 + 5s tail = ~6s
1 小时心跳 120 次 → 占用 720s = 12 分钟高功耗
通信芯片 24 小时占用 = 24 × 12 / 60 = 4.8 小时(20%)
2
3
这就是为什么"30s 心跳"会让 §17.1 实验 数据显示 28mAh,比 5min 心跳的 10mAh 高 3 倍。
# 9.3 5G 的功耗陷阱
直觉:"5G 更快应该更省(race-to-idle)"。实际情况完全相反:
| 场景 | 4G 耗电 | 5G 耗电 | 谁赢 |
|---|---|---|---|
| 100KB 小数据 | 25mA | 40mA | 4G 赢 |
| 10MB 中数据 | 80mA | 90mA | 5G 略赢 |
| 100MB 大文件 | 持续 80mA × 30s | 持续 90mA × 6s | 5G 大赢 |
| 待机(不发数据) | 15mA | 25mA | 4G 大赢 |
核心认知:5G 在"持续大流量"才赢,"小数据 + 待机"反而 5G 费电。这是因为 5G 的天线阵列更多、信号处理更复杂、tail time 更长。
应用层的应对:
// Android 11+:可以提示偏好的网络类型
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build()
// 但实际网络选择由系统决定,应用只能"提示"
2
3
4
5
6
探索性思考:为什么运营商和 OS 不"自动给小数据降到 4G"? 因为网络切换本身就是高耗操作——LTE 和 5G 共存时切换成本比保持 5G 高。所以系统通常"维持现状"。应用唯一能做的是减少不必要的网络请求——少发就是省。这是 §14.1 批量化的根本动机。
# 9.4 长连接 vs push 的选择
| 方案 | 心跳成本 | 实时性 | 跨平台 | 适用场景 |
|---|---|---|---|---|
| 自建长连接 | 高(独占) | 极高 | 跨平台 | 即时通讯、游戏 |
| 系统 push(FCM/APNs/厂商) | 低(系统共享) | 中(秒级延迟) | 各端独立 | 通知、状态同步 |
| 短轮询 | 极高 | 低 | 跨平台 | 不应使用 |
关键洞察:FCM/APNs 是"系统级长连接"——所有应用共享一根连接、一份心跳。一台手机装 100 个应用、只走 1 份系统心跳,比 100 份应用各跑各的心跳省电 N 倍。
国内特殊性:FCM 在国内不可用,导致每个应用各自实现长连接,这是 Android 国行机后台耗电高于海外的物理根因。统一推送联盟和厂商推送(小米/华为/OPPO/VIVO Push)就是为解决这个问题。
探索性思考:为什么"应用自轮询"在 iOS 上几乎不存在? 因为 iOS 后台严格——应用切到后台 30s 内必须停所有网络(除非有特定 background mode)。应用想轮询也轮询不了。所以 iOS 应用从一开始就被迫用 APNs,反而生态更省电。这是 OS 强约束带来的"良性副作用"——见 §11.2。
# 10.定位与传感器 ⭐
本章把定位功耗从"GPS 卫星信号"一路拆到"应用层 LocationRequest API",回答:GPS 为什么是后台电池黑洞 / 传感器融合怎么省电 / iOS Significant Location Change 的物理原理。
# 10.1 定位的物理根源
定位有四种技术,功耗差别巨大:
| 技术 | 功耗 | 精度 | 启动时间 | 物理原理 |
|---|---|---|---|---|
| GPS(卫星) | 30-50mA | 5m | 30s 冷启动 | 接收 4+ 颗卫星信号解算 |
| A-GPS | 30-50mA | 5m | 5-10s | 用网络辅助下载星历加速 |
| 网络定位 | 5-10mA | 50-200m | 1-3s | WiFi MAC + 基站 → 服务端查询 |
| 被动定位 | < 1mA | 依赖其他 App | 0s | "搭车"其他 App 的定位结果 |
关键认知:GPS 是定位中的"重炮"——精度高但功耗也最高。竞品省电的秘密是绝大多数场景用网络定位 + 关键时刻才用 GPS。
# 10.2 GPS 工作原理与冷启动代价
GPS 接收机工作流程:
1. 搜星:扫描天空中的可见 GPS 卫星(30s)
2. 下载星历:每颗卫星每 12.5 分钟广播一次完整星历(30s)
3. 解算:至少 4 颗卫星 + 时间同步 → 三维位置
4. 持续跟踪:保持锁定(持续耗电)
2
3
4
冷启动(无任何缓存):30s+ 温启动(有星历但位置变了):5-10s 热启动(有星历且位置接近):1-3s
A-GPS 的妙处:通过网络下载星历(几 KB 数据),跳过"30s 等卫星广播"——网络代价 << GPS 代价。这是为什么"开 GPS 时也开网络反而更省电"。
探索性思考:为什么后台 GPS 持续是"电池黑洞"? GPS 接收机一旦打开就持续耗电——它必须不停接收卫星信号才能保持锁定。一旦关闭,再开就要冷启动。所以"持续定位 30 分钟" vs "每 5 分钟开关一次 30s",前者反而更省——这与"批量唤醒"思路相反。这是 GPS 的特殊性:tail time 极短但启动成本高。应用应该"长开 + 降精度"而非"短开 + 高精度"。
# 10.3 传感器融合的工程妙处
┌─ 加速度计(< 0.1mA):判断是否在移动
│
├─ 陀螺仪(< 0.5mA):判断方向变化
│
├─ 磁力计(< 0.1mA):判断指南针方向
│
├─ 气压计(< 0.5mA):判断楼层
│
└─ → 融合算法 → "现在没移动" → 暂停 GPS(省 30-50mA)
→ "正在直行" → 低频 GPS(省一半)
→ "转弯" → 高频 GPS(高精度)
2
3
4
5
6
7
8
9
10
11
关键 API:
// Android Activity Recognition(系统级,免费)
val client = ActivityRecognition.getClient(context)
client.requestActivityUpdates(detectionInterval, pendingIntent)
// 系统识别 IN_VEHICLE / WALKING / STILL,应用据此调整定位策略
// iOS Core Motion
let manager = CMMotionActivityManager()
manager.startActivityUpdates(to: queue) { activity in
if activity?.stationary == true {
// 静止,停 GPS
}
}
2
3
4
5
6
7
8
9
10
11
12
这是 §02 案例 方法派 Day 4 省 50% 的核心机制——直行用低频 GPS、转弯用高频、静止时停 GPS。
# 10.4 iOS Significant Location Change 的妙处
iOS 提供的特殊定位 API:
let manager = CLLocationManager()
manager.startMonitoringSignificantLocationChanges()
// 仅在用户位置发生"显著变化"(500m+)时唤醒应用
2
3
物理原理:iOS 利用基站切换和WiFi 网络变化作为"位置变化信号"——这两类信号系统本来就要监听(接电话、网络切换),不增加额外功耗。应用只在系统检测到变化时才被唤醒。
与持续 GPS 对比:
| 方案 | 1 小时功耗 | 精度 | 适用场景 |
|---|---|---|---|
| 持续 GPS | 30-50mAh | 5m | 导航、运动跟踪 |
| Significant Location | < 1mAh | 500m | 区域提醒、上下班识别 |
探索性思考:为什么 Android 没有同等机制? Android 有 GeofencingClient,但实现质量参差不齐——不同 ROM 实现差异大,国内厂商 ROM 经常杀进程。所以 Android 上"区域提醒"类应用必须自实现传感器融合 + 定时低频 GPS。这就是 iOS 在功耗上系统性优于 Android 的一个具体例子——强 OS 约束 + 统一 API 实现 = 全平台省电。
# 11.后台调度全链路 ⭐
本章把后台调度从"系统省电策略"一路拆到"应用层 WorkManager API",回答:Doze 怎么强制批量 / iOS BGTaskScheduler 为什么这么设计 / Web 后台标签怎么节流。
# 11.1 Android Doze + WorkManager 原理
Doze 模式的工程设计是让所有应用的后台任务"集体休眠 + 集体批量":
[屏幕灭 + 静止 1 小时]
↓
──┬─ Doze active(短)───┬─ 维护窗口(短)─┬── ...
│ │ │
网络/Job/Alarm 暂停 释放压抑的任务 继续暂停
2
3
4
5
维护窗口的间隔越来越长:30 分钟 → 1 小时 → 2 小时 → 6 小时——长期不动手机的应用最终几乎每 6 小时才有一次执行机会。
WorkManager 配合姿势:
val work = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // 仅 WiFi
.setRequiresCharging(true) // 仅充电
.setRequiresBatteryNotLow(true) // 电量充足
.setRequiresDeviceIdle(true) // Doze 期间执行
.build())
.build()
WorkManager.getInstance(context).enqueue(work)
2
3
4
5
6
7
8
9
核心思想:告诉系统"我什么条件下能跑",让系统找最经济的时机。
# 11.2 iOS BGTask 与 Jetsam
iOS 的后台哲学完全不同:默认应用切到后台 30s 后所有线程被挂起。要后台执行必须申请:
// 短任务:30s 内完成
let task = UIApplication.shared.beginBackgroundTask {
// 30s 到期被强制结束
}
// 后台 fetch:iOS 13+ 改用 BGTaskScheduler
let request = BGProcessingTaskRequest(identifier: "com.app.sync")
request.requiresExternalPower = true // 仅充电
request.requiresNetworkConnectivity = true
BGTaskScheduler.shared.submit(request)
// 系统在最经济时机调度(可能几小时后)
2
3
4
5
6
7
8
9
10
11
Jetsam(OOM Killer)原理:iOS 内存压力时按 priority 杀进程:
| Priority | 类型 | 杀的概率 |
|---|---|---|
| FOREGROUND | 当前前台 | 极低 |
| FOREGROUND_BAND | 前台普通 | 低 |
| BACKGROUND | 后台 | 中 |
| IDLE | 完全 idle | 高 |
应用层启示:后台占内存越少越不容易被杀——这是 iOS 后台留存的最大原则。
探索性思考:为什么 iOS 设计"后台 30s 强杀"? 因为 Apple 早期判断:"后台多任务必然导致功耗失控"——所以完全禁止后台默认运行。开发者要后台必须申请特定 API + 系统统一调度。这是 iOS 续航长的根本原因——不靠用户和开发者自觉,靠 OS 强制。
# 11.3 厂商 ROM 后台清理的工程现实
国内 Android 厂商 ROM(小米/华为/OPPO/VIVO)在 Doze 之上还加了一层"激进省电":
| 行为 | 表现 | 应用应对 |
|---|---|---|
| 杀后台进程 | 切后台 N 分钟后被杀 | 推送+保活,但保活越来越难 |
| 限制自启动 | 应用从后台无法被启动 | 用户引导加白名单 |
| 关联唤醒限制 | A 应用拉起 B 应用受限 | 用 push 直接触达 |
| 频繁唤醒检测 | 唤醒次数高的被压制 | 降低唤醒频率(本节核心) |
关键洞察:厂商 ROM 的限制本质上是"被应用滥用逼出来的"——应用越守规矩,限制越宽松。这就是为什么"省电"和"留存"是同一个问题——省电的应用不容易被 ROM 杀。
# 11.4 Web 后台标签节流原理
浏览器对后台标签做强力节流:
| 浏览器 | 后台 setTimeout 节流 | 后台 setInterval 节流 | 后台 requestAnimationFrame |
|---|---|---|---|
| Chrome | 最多 1Hz | 最多 1Hz | 暂停 |
| Safari | 暂停 | 暂停 | 暂停 |
| Firefox | 1Hz | 1Hz | 暂停 |
bfcache(Back/Forward Cache):用户切走标签后,浏览器把整个页面 freeze(保留 DOM 但不执行 JS),返回时秒恢复。这是浏览器层的"应用休眠"。
Web 应用的省电姿势:
// 监听 visibility,主动暂停业务
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
clearInterval(myTimer); // 停心跳
videoPlayer.pause(); // 停视频
} else {
myTimer = setInterval(...); // 恢复
}
});
// Page Visibility + Wake Lock:仅在前台时保持屏幕亮(如视频播放)
const wakeLock = await navigator.wakeLock.request('screen');
2
3
4
5
6
7
8
9
10
11
12
探索性思考:为什么 Web 浏览器对 JS 节流这么激进? 因为浏览器的"应用模型"是多 Tab 共享一个进程(早期),单个失控 Tab 能拖死整个浏览器。所以浏览器必须做"严格的资源隔离 + 后台限制"。这反过来给 Web 应用带来约束——任何"后台逻辑"都不可信。这就是为什么 Web 应用做"实时同步"必须用 WebSocket + 服务端推送,而非客户端轮询——见 §09.4。
# 12.跨端功耗对照
# 12.1 端到端流程对照表
| 阶段 | Android | iOS | Web |
|---|---|---|---|
| 后台调度 API | WorkManager / JobScheduler | BGTaskScheduler | Service Worker / requestIdleCallback |
| 强省电模式 | Doze + App Standby | Low Power Mode | bfcache + 标签节流 |
| 唤醒成本 | tail time + 唤醒锁 | tail time + 唤醒成本 | JS 引擎冷启 |
| 定位省电 API | FusedLocationProvider | Significant Location | Geolocation watchPosition |
| 物理台测试 | Battery Historian + Monsoon | Energy Log + Monsoon | Performance API(间接) |
| 线上监控 | batterystats + ApplicationExitInfo | MetricKit | Beacon + Performance |
# 12.2 占比与可干预度对比
| 模块 | Android 可干预 | iOS 可干预 | Web 可干预 |
|---|---|---|---|
| 屏幕 | 中(暗黑/刷新率) | 中 | 低(仅 Wake Lock) |
| CPU | 高(线程/绑核) | 中(QoS) | 低(Web Worker) |
| 通信 | 高(心跳/批量) | 中(受系统约束) | 中(Service Worker) |
| 定位 | 高(自定义策略) | 高(Significant LC) | 低(API 受限) |
| 后台 | 中(受 Doze + ROM) | 极低(强约束) | 极低(节流) |
# 12.3 统一启示
- 功耗治理本质是"配合 OS 而非对抗 OS"——iOS 严格管控反而生态更省电。
- 三大主战场普适:屏幕(受用户)+ 通信(应用可控)+ 定位(应用可控)。
- iOS 的"约束 = 福音":BGTaskScheduler / Significant Location 是 Android 没有的"系统级省电 API"。
- 跨端策略相同:少 + 批 + 让 + 轻 在所有端都成立,差别只在 API。
- 国内 Android 是"特殊战场":FCM 不可用 + 厂商 ROM 杀进程,导致"省电=活久"。
# 13.治理一层少
本节回答四个递进问题:①如何减少必要任务?②如何让任务批量?③如何让系统决策时机?④如何选低功耗硬件路径? §13-§16 由浅入深四层。
# 13.1 一层命题
核心命题:§02 案例 砍掉 60% 非核心任务是第一大杠杆。"少"是最被低估的优化思想——因为它要求和业务方对抗。
层级特征:
- 改造成本:低(删代码 / 改 SDK 配置)
- 收益:极高(典型砍 40-60% 后台任务)
- 风险:中(业务方可能反对)
- 是否必做:所有应用
# 13.2 策略 1.1:后台任务清单审查
机理:应用上线 2-3 年后,后台任务必然累积——每个 SDK、每个功能、每个 A/B 实验都在加。没有清单管理,最终就是"无主任务森林"。
做法:
// 1. 列出所有后台执行点
// - Service / IntentService / JobIntentService
// - JobScheduler / WorkManager
// - BroadcastReceiver(开机/网络变化等)
// - AlarmManager
// - 第三方 SDK 注入(推送、统计、广告)
// 2. 按"业务必要性 × 用户感知"分类
// - 必要 + 高感知:保留
// - 必要 + 低感知:批量化(→ §14)
// - 不必要 + 任意:砍
2
3
4
5
6
7
8
9
10
11
典型砍掉项目:
- 重复的崩溃监控 SDK(保留 1 个)
- 关闭功能但未下线的 Service
- 上线 2 年没人维护的实验代码
- 多个 SDK 各自的位置上报
收益:§02 案例 后台耗电 -50%。
# 13.3 策略 1.2:用 push 替代轮询
机理:§09.4 解释了系统级 push(FCM/APNs/厂商)vs 应用自轮询的本质差距——前者是"系统共享一份心跳",后者是"每个应用各自心跳"。
做法:
| 场景 | 旧方案 | 新方案 |
|---|---|---|
| 通知到达 | 应用 30s 轮询 | FCM/APNs 推送 |
| 状态同步 | 5min 拉一次最新 | 服务端推送 + 增量同步 |
| 实时聊天 | 自建长连接 | 系统 push 唤醒 + 短连接拉 |
收益:心跳消耗清零;推送到达率反而升。
边界:海外用 FCM、国内用厂商推送融合 SDK(小米/华为/OPPO/VIVO 各家+统一推送联盟)。必须做厂商推送融合——单一推送在某品牌手机上到达率可能只有 30%。
# 13.4 策略 1.3:WakeLock 强制超时
机理:§06.2 元凶。WakeLock 一旦"持有忘释放"就永久阻止 CPU 进 deep sleep——24h 持锁能耗增加 5-10× 是常见事故。
做法:
// ❌ 错误:可能忘释放
wakeLock.acquire();
doWork();
wakeLock.release(); // 异常时跳过
// ✅ 正确:强制超时 + try/finally
wakeLock.acquire(10 * 60 * 1000L); // 10 分钟硬上限
try {
doWork();
} finally {
if (wakeLock.isHeld()) wakeLock.release();
}
2
3
4
5
6
7
8
9
10
11
12
收益:防"持有忘释放"长期持锁。
边界:业务方需理解 timeout 后任务可能被打断。这是设计取舍——宁可任务失败重试,也不能锁电池。
# 13.5 一层反思
探索性思考:为什么"少"是最难做的? 因为它没有技术难度,只有政治难度——你必须说服业务方"这个功能可以砍"。技术上"批量化"虽难但 PM 不反对(用户没感知),而"砍功能"是直接挑战需求。经验法则:用功耗数据 + 用户投诉数据双向论证——"这个功能让用户耗电 +5%、卸载率 +1%"。功耗治理本质是产品工程合作问题,不是纯技术问题。
# 14.治理二层批
# 14.1 二层命题
核心命题:§17.3 实验 批量省 88%。
层级特征:
- 改造成本:低(用 JobScheduler/WorkManager 包装)
- 收益:极高(80-90% 通信/CPU 占用)
- 风险:低
- 适用范围:所有非紧急任务
# 14.2 策略 2.1:网络请求批量化
机理:§09.2 RRC 状态机——每次请求触发 1s 握手 + 5s tail,100 次请求逐个发 vs 一次发完,差距 10×。
做法:
// 业务请求"非紧急"时入批量队列
class BatchedRequest {
private val pending = mutableListOf<Request>()
fun enqueue(req: Request, urgent: Boolean = false) {
if (urgent) sendImmediately(req)
else {
pending.add(req)
scheduleBatchSend() // 1-2s 防抖窗口或量到 10 个
}
}
private fun scheduleBatchSend() {
// WorkManager 调度,可加约束(仅 WiFi 等)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
收益:通信 tail time 大幅下降;§17.3 实验 100 次 vs 1 次省 88%。
边界:紧急请求要单独通道(用户操作直接反馈)。关键判断:"这个请求晚 1-5 秒发出去用户感知吗?"——感知不到就批量。
# 14.3 策略 2.2:定位 + 传感器融合
机理:§10.3 传感器融合——用 < 1mA 的传感器判断"是否需要 GPS",省下 30-50mA 的 GPS 持续耗电。
做法:
// 直行用低频 GPS(10s/次),转弯/高速时用高频
val locationRequest = LocationRequest.create()
.setPriority(if (isFastMoving) PRIORITY_HIGH_ACCURACY else PRIORITY_BALANCED_POWER_ACCURACY)
.setInterval(if (isFastMoving) 2000L else 10000L)
// 用 Activity Recognition 判断状态
ActivityRecognition.getClient(context)
.requestActivityUpdates(60_000, pendingIntent)
// IN_VEHICLE → 高频;STILL → 暂停 GPS
2
3
4
5
6
7
8
9
收益:§02 案例 定位耗电 -50%。
边界:需用加速度传感器/系统 Activity Recognition 判断"是否在移动"。注意 GPS 启动有冷热成本——见 §10.2:长开 + 降精度比短开 + 高精度更省。
# 14.4 策略 2.3:AlarmManager.setInexactRepeating
机理:精确 Alarm 让 CPU 在精确时刻唤醒——多个应用各自精确,CPU 永远在 active 态。不精确 Alarm 让系统批量唤醒所有应用——一次唤醒处理 N 个应用的任务。
做法:
// ❌ 错误:精确 + 频繁
alarmManager.setExactAndAllowWhileIdle(RTC_WAKEUP, triggerTime, pi)
// ✅ 正确:不精确 + 批量
alarmManager.setInexactRepeating(AlarmManager.RTC, triggerTime, INTERVAL_HOUR, pi)
// ✅ 更现代:用 WorkManager(推荐)
val work = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(...)
2
3
4
5
6
7
8
9
收益:系统可批量唤醒多个 App 的 Alarm,省电 50%+。
边界:精确时间任务(如倒计时)仍需 setExact。90% 的业务需求都不需要精确——仔细评估"晚几分钟到底有没有问题"。
# 14.5 二层反思
探索性思考:为什么"批量化"能颠倒数学直觉? 因为人的直觉是"成本平摊"——100 次请求是 1 次的 100 倍。但通信芯片有"启动成本"和"保持成本"——一次启动 6 秒成本,无论你发 1 个包还是 100 个包都一样。所以批量化的本质是"摊薄启动成本",类似经济学的"规模效应"。这种"非线性 → 批量化"的思维在系统设计无处不在——HTTP 复用连接、磁盘 IO 合并、消息队列等。
# 15.治理三层让
# 15.1 三层命题
核心命题:系统知道全局资源状态(电量、温度、网络、用户使用模式),让它决策比应用自决策更优。"让"是哲学转变——从"应用主动控制"转为"应用提交意图、系统决定"。
层级特征:
- 改造成本:中(要重新设计任务调度)
- 收益:高(系统级省电的红利)
- 风险:低
- 思想来源:§04.5 跨平台同构原理
# 15.2 策略 3.1:WorkManager + Constraints(Android)
机理:告诉系统"我什么条件下能跑",系统在最经济时机执行。
做法:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // 仅 WiFi
.setRequiresCharging(true) // 仅充电
.setRequiresBatteryNotLow(true) // 电量充足
.setRequiresDeviceIdle(true) // 设备空闲
.build()
val work = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.DAYS)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"daily_sync", ExistingPeriodicWorkPolicy.KEEP, work
)
2
3
4
5
6
7
8
9
10
11
12
13
14
收益:在最经济时段执行任务(充电 + WiFi + 空闲)。
边界:调度延迟可能 10s-小时,实时性差任务不适用。但 80% 的"后台同步、日志上报、缓存清理"都可以接受。
# 15.3 策略 3.2:Doze 模式配合
机理:§08.4 Doze 原理 是系统级省电的范式。应用应主动配合,不要试图绕过——绕过只会被系统降权(电量优化白名单 + ROM 杀进程)。
做法:
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| 申请 ignore battery optimization 白名单 | 不申请,靠 push 触达 |
| setExactAndAllowWhileIdle 精确唤醒 | setInexactRepeating + WorkManager |
| 自实现长连接绕过 Doze | 用 FCM / 厂商推送 |
| 后台 Service + WakeLock 长持 | 短任务 + 系统调度 |
关键认知:Doze 不是 bug,是 feature——它是"应用恶性竞争"的终结者。
# 15.4 策略 3.3:iOS BGProcessingTask + requiresExternalPower
机理:§11.2 iOS BGTask 让系统在最经济时机调度任务(通常充电时)。
做法:
// 注册(didFinishLaunching)
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
// 提交请求
let request = BGProcessingTaskRequest(identifier: "com.app.sync")
request.requiresExternalPower = true // 仅充电
request.requiresNetworkConnectivity = true
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
do {
try BGTaskScheduler.shared.submit(request)
} catch { print("submit fail: \(error)") }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
收益:避免后台 fetch 频繁唤醒;任务在充电+WiFi 时执行。
边界:iOS 后台 API 越精细,误用代价越大——误用会导致系统"惩罚式"降低你的调度优先级。
# 15.5 策略 3.4:低电量模式自适应
机理:用户主动开启低电量模式时,应用应主动降级配合——这是与系统的协作。
做法:
// Android
val powerManager = context.getSystemService(PowerManager::class.java)
if (powerManager.isPowerSaveMode) {
reduceAnimations()
disablePrefetch()
increaseHeartbeatInterval()
}
// 监听变化
val filter = IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
registerReceiver(receiver, filter)
2
3
4
5
6
7
8
9
10
11
// iOS
if ProcessInfo.processInfo.isLowPowerModeEnabled {
AnimationManager.shared.disableAll()
NetworkManager.shared.minimizeBackgroundActivity()
}
2
3
4
5
收益:低电量场景体验保护 + 省电。
边界:需业务方对齐降级策略——不要默默关闭核心功能。
# 15.6 三层反思
探索性思考:为什么"让系统决策"是范式转变? 移动开发的早期文化是"应用主权"——应用想干什么就干什么。但这导致了"公地悲剧"——每个应用都觉得自己重要、都最大化自己的后台权限,最后用户电池一天都撑不住。Doze、BGTaskScheduler、Tab 节流是 OS 的"反公地"政策——强制把"应用主权"收回到"系统主权"。应用越早转变思维(从主权到协作),越能在新生态生存。
# 16.治理四层轻
# 16.1 四层命题
核心命题:§17.2 + §17.4 + §17.5 证明选硬件路径有 3-10× 差距。"轻"是终极手段——选低功耗硬件、降级到合适精度。
层级特征:
- 改造成本:中-高(要做硬件检测 + 多路径实现)
- 收益:极高(3-10×)
- 风险:中(兼容性 + 体验权衡)
- 适用:核心功能
# 16.2 策略 4.1:定位优先级(被动 > 网络 > GPS)
机理:§10.1 三种定位技术功耗差 5-70 倍。
做法:
// 优先级递降
fun chooseLocationStrategy(business: Business) = when {
business.needsRealTimePrecise -> PRIORITY_HIGH_ACCURACY // 导航
business.needsApproximate -> PRIORITY_BALANCED_POWER // 天气、新闻
else -> PRIORITY_LOW_POWER // 仅城市级
// 或者更省:listen passive
}
// iOS:根据场景选 API
if business == .geofence {
manager.startMonitoringSignificantLocationChanges() // < 1mA
} else if business == .navigation {
manager.desiredAccuracy = kCLLocationAccuracyBest // GPS 全开
}
2
3
4
5
6
7
8
9
10
11
12
13
14
收益:§17.2 实验 4-70× 差距。
边界:业务必须的高精度场景仍需 GPS。关键判断:用户是否真的需要 5m 精度?大多数"基于位置的服务"50-200m 足够。
# 16.3 策略 4.2:暗黑模式(OLED 设备)
机理:§07.1 屏幕物理——OLED 像素不亮就不耗电,黑色像素省 60% 屏幕功耗;LCD 背光全亮,暗黑无效。
做法:
fun isOledDevice(): Boolean {
// 1. 通过设备型号查询(维护机型库)
// 2. 或通过 Display.HdrCapabilities 间接推测(OLED 通常支持 HDR)
return DEVICE_MODEL_DB.isOled(Build.MODEL)
}
if (isOledDevice() && !userPreferDark) {
suggestDarkMode() // 仅 OLED 推荐
}
2
3
4
5
6
7
8
9
收益:§17.4 实验 OLED 设备 -58%。
边界:LCD 无效,不要误导用户。很多 App 在所有设备上推荐暗黑——这就是反模式。
# 16.4 策略 4.3:网络选择(小数据 4G/WiFi、大数据 5G)
机理:§09.3 5G 功耗陷阱——5G 在大数据传输时省(race-to-idle),小数据时反而费。
做法:
// 优先 WiFi,其次根据数据量选蜂窝网络
fun selectNetwork(dataSize: Long): Network {
if (wifiAvailable()) return wifiNetwork
return if (dataSize > 10 * 1024 * 1024) {
cellular5G ?: cellular4G // 大文件用 5G
} else {
cellular4G // 小数据用 4G
}
}
2
3
4
5
6
7
8
9
收益:日常小请求功耗下降 30-40%。
边界:网络选择 API 部分系统不支持——实际工程中很难做。可控点是"减少不必要的网络请求",而非"切换网络类型"。
# 16.5 策略 4.4:传感器精度自适应
机理:§10.3 传感器融合。
做法:
// 计步场景:根据电量自适应
val sampleRate = when {
batteryLevel < 0.2 -> SensorManager.SENSOR_DELAY_NORMAL // 200ms
batteryLevel < 0.5 -> SensorManager.SENSOR_DELAY_UI // 60ms
else -> SensorManager.SENSOR_DELAY_GAME // 20ms
}
sensorManager.registerListener(listener, accelerometer, sampleRate)
2
3
4
5
6
7
收益:传感器功耗 -30-60%(采样率降低对计步业务影响小)。
边界:高频传感器场景(如游戏)不可降。
# 16.6 优先级判定(ROI)
| ROI | 优化项 | 收益 | 成本 | 风险 | 对应策略 |
|---|---|---|---|---|---|
| 极高 | 后台任务清单审查 + 砍 60% | 后台耗电 -50% | 1-2 周 | 中(业务对齐) | §13.2 |
| 极高 | 用 push 替代轮询 | 心跳消耗清零 | 1-2 周 | 中 | §13.3 |
| 极高 | 心跳间隔 5-10 分钟 | 心跳耗电 -65% | 几天 | 低 | §14.1 |
| 极高 | 定位降级(被动/网络优先) | 定位耗电 -70%+ | 1-2 周 | 中(业务) | §16.2 |
| 高 | 网络请求批量 + WorkManager | 通信 -80% | 1-2 周 | 低 | §14.2 + §15.2 |
| 高 | 传感器融合智能定位 | 高精度场景省 50% | 2-3 周 | 中 | §14.3 |
| 高 | WakeLock 强制 timeout | 防长期持锁 | 几天 | 低 | §13.4 |
| 中 | AlarmManager 改 setInexact | 唤醒批量化 | 几天 | 低 | §14.4 |
| 中 | OLED 暗黑模式推荐 | 屏幕 -58% | 1 周 | 低 | §16.3 |
| 中 | 低电量模式自适应 | 极端场景体验 | 1 周 | 中 | §15.5 |
| 中 | iOS BGProcessingTask 用对 | 后台不打挂电池 | 1 周 | 中 | §15.4 |
| 低 | 自实现功耗 SDK | 极少收益 | 极高 | 高 | - |
避免反向收益:
- 降屏幕亮度治耗电:§02 案例 第 1 周翻车(屏幕不是主战场)。
- 关功能治耗电:第 2 周翻车(用户怒了)。
- 降帧率治耗电:第 3 周翻车(地图卡顿)。
- 绕过 Doze:被系统降权。
- AlarmManager.setExact 用于非紧急:电池杀手。
# 16.7 四层反思
探索性思考:为什么"轻"层在国内 Android 特别难做? 因为国内 Android 设备碎片化极强——OLED/LCD 比例 7:3、各家 ROM 对 Doze 的实现千差万别、5G 网络部署不均。应用要做"硬件适配"成本极高。但反过来,这正是有功耗治理能力的应用的护城河——大部分应用做不到,做到的应用就能在差评里脱颖而出。
# 17.求证实验
# 17.1 实验一:心跳间隔的甜区
Step 1 — 原始观察:所有需要长连接的应用都要选心跳间隔——多长最优?
Step 2 — 提出疑问:30 秒和 5 分钟的功耗差距很大;5 分钟之后是否边际收益递减?
Step 3 — 设计实验:固定其他变量,仅改变心跳间隔,物理台测 1h 通信耗电。
Step 4 — 实测数据:
| 心跳间隔 | 1h 通信耗电(mAh) | 相对 30s |
|---|---|---|
| 30s | 28 | 1.0× |
| 1min | 18 | 0.64× |
| 3min | 12 | 0.43× |
| 5min | 10 | 0.36× |
| 10min | 9.5 | 0.34× |
| 30min | 9 | 0.32× |
Step 5 — 提炼结论:
心跳间隔 ≥ 5 分钟,再长边际收益小但失去保活意义;最佳甜区 5-10 分钟。30s 心跳功耗是 5min 的近 3 倍。
Step 6 — 边界:
- 智能心跳(NAT 探测)可动态调整——网络稳定时延长,弱网时缩短。
- 不同 NAT 网关 keepalive 阈值不同(运营商 5min、企业网 1min)。
- iOS 自建长连接超 30s 后台必被挂起,所以 iOS 主要靠 APNs。
# 17.2 实验二:定位精度的代价
Step 1 — 原始观察:§02 案例 中 GPS 是 38% 耗电源。不同定位技术差距究竟多大?
Step 2 — 提出疑问:高精度 GPS、网络定位、被动定位耗电差几倍?
Step 3 — 设计实验:1 小时分别用三种定位策略,物理台测耗电。
Step 4 — 实测数据:
| 定位方式 | 1h 耗电(mAh) | 精度 | 相对被动 |
|---|---|---|---|
| GPS PRIORITY_HIGH_ACCURACY | 35 | 5m | 70× |
| GPS + 网络(BALANCED) | 12 | 20m | 24× |
| 网络定位(LOW_POWER) | 8 | 50m | 16× |
| 被动定位(PASSIVE) | 0.5 | 依赖其他 App | 1× |
| iOS Significant Location | < 1 | 500m | 2× |
Step 5 — 提炼结论:
GPS vs 被动定位差 70 倍。除非业务必须,绝不用 GPS 后台持续定位。优先级:被动 → 网络 → GPS。
Step 6 — 边界:
- 被动定位依赖其他 App 在用 GPS——是"搭便车",不能保证时机。
- iOS Significant Location 仅在位置变化 500m+ 时唤醒——不适合精确轨迹。
- 室内 GPS 失效,必须用 WiFi/蓝牙 beacon 定位。
▶▶ 回扣 §02 案例:本实验"GPS 35mAh vs 网络 8mAh"是导航 App 38% 耗电源的物理证据。方法派 Day 4 用"传感器融合 + 低频 GPS"省电 50%,正是本实验工程意义的扩展应用。
# 17.3 实验三:批量唤醒的收益
Step 1 — 原始观察:§04.2 tail time 解释了批量化的数学原理。实测差距多大?
Step 2 — 提出疑问:100 次小请求逐次发 vs 批量打包一次发,省电多少?
Step 3 — 设计实验:相同数据量(100 个 1KB 请求),分别用两种发送策略。
Step 4 — 实测数据:
| 策略 | 总耗电(mAh) | 总耗时(s) | 省电 |
|---|---|---|---|
| 逐次发送(每次间隔 30s) | 56 | 3000 | 基准 |
| 批量打包一次发 | 7 | 5 | 省 88% |
| 批量 + 等 WiFi | 4 | 取决于 WiFi 时机 | 省 93% |
Step 5 — 提炼结论:
所有非紧急请求必须用 JobScheduler/WorkManager 批量调度。100 次 vs 1 次省 88%。Doze 模式下系统会强制批量,符合系统期望反而双赢。
Step 6 — 边界:
- 紧急请求(用户操作直接反馈)需单独通道。
- 批量窗口 1-5s 是经验值,过长用户感知延迟。
# 17.4 实验四:屏幕亮度 + OLED 暗黑模式的真实功耗
Step 1 — 原始观察:§07.1 屏幕物理——OLED 和 LCD 物理机制完全不同。实测暗黑模式收益差多少?
Step 2 — 提出疑问:调暗屏幕 / 暗黑模式 / 自动亮度,省电效果如何?
Step 3 — 设计实验:相同应用界面,分别在 OLED 和 LCD 设备上测 1h 屏幕耗电。
Step 4 — 实测数据:
| 模式 | OLED 屏 1h 耗电 | LCD 屏 1h 耗电 |
|---|---|---|
| 100% 亮度 + 白色背景 | 12% | 13% |
| 50% 亮度 + 白色 | 7% | 8% |
| 100% 亮度 + 暗黑(OLED 像素不亮) | 5% | 12%(LCD 背光仍全亮) |
| 自动亮度(户外低亮度) | 6% | 7% |
Step 5 — 提炼结论:
OLED 设备暗黑模式真省电(-58%);LCD 设备暗黑模式无效。判断设备屏幕类型后做差异化推荐。
Step 6 — 边界:
- LCD 推暗黑反而误导用户(白白难看)。
- 部分 OLED 屏长期黑色像素会"烧屏"——需用户接受。
# 17.5 实验五:5G vs 4G vs WiFi 的功耗对比
Step 1 — 原始观察:§09.3 5G 陷阱——race-to-idle 在 5G 上未必成立。实测对比?
Step 2 — 提出疑问:5G 通信芯片功耗高,什么场景下应主动用 4G/WiFi?
Step 3 — 设计实验:固定数据量,分别用三种网络。
Step 4 — 实测数据:
| 网络 | 待机 | 100KB 下载 | 10MB 下载 | 100MB 下载 |
|---|---|---|---|---|
| WiFi | 5mA | 10mA | 60mA | 持续 80mA × 30s |
| 4G | 15mA | 25mA | 80mA | 持续 80mA × 60s |
| 5G | 25mA | 40mA | 90mA | 持续 90mA × 6s(总能耗最低) |
Step 5 — 提炼结论:
小数据用 4G/WiFi 反而省电;5G 仅适合大数据传输。
Step 6 — 边界:
- 应用很难强制网络选型——OS 自动决策。
- 应用可控的是"减少不必要请求",而非"切网"。
# 17.6 五大实验启示
心跳间隔 → 5-10 分钟甜区,30s 耗电 3× ─┐
│
定位精度 → GPS vs 网络 4×、vs 被动 70× │
│
批量唤醒 → 100 次 vs 1 次省 88% ├─▶ 功耗 = 少 + 批 + 让 + 轻
│
屏幕 + 暗黑 → OLED 暗黑省 58%、LCD 无效 │
│
5G 功耗陷阱 → 小数据用 5G 反而费电 ─┘
2
3
4
5
6
7
8
9
统一启示:
- 不要凭直觉,用物理台量化——经验派 §02 案例 失败的根本原因。
- 批量化是数学必然——tail time 决定的,不是工程偏好。
- 硬件路径选择是 3-10× 杠杆——比微观优化收益大得多。
- OS 配合 > 对抗——所有"绕过 Doze"的方案最后都失败。
- 非线性 / 阶梯式回报——心跳从 30s 到 5min 收益巨大,5min 后边际几乎为 0。
# 18.实战案例
# 18.1 跨端同构案例·心跳风暴
某 App 长连接断线后重连策略是"每 5 秒重试"。一旦遇到弱网长时间不通,连续重试 1 小时,耗电飙升 5×。
根因(用 §09.2 RRC 分析):每次重试触发 1s 握手 + 5s tail = 6s 高耗,1 小时重试 720 次,通信芯片几乎 100% active。
修法:指数退避 5s/10s/30s/1min/5min;上限 5 分钟。
教训:所有重试都必须有指数退避 + 上限。"等到网络好"的逻辑要靠 ConnectivityManager 监听网络变化主动触发,而非定时重试。
# 18.2 平台特异案例·iOS Background Fetch 滥用
某 iOS App 把数据同步全放 Background Fetch,结果系统智能调度后频繁唤醒,电池被吃光。
根因(用 §11.2 BGTask 分析):Background Fetch 系统会"学习"用户使用模式——用户每天打开 10 次,系统就唤醒 10 次让你 prefetch。每次唤醒成本 ~5s × 10 次 = 50s 高耗 / 天。
修法:
- 核心同步用 push 触发(FCM/APNs 立即唤醒)。
- 非核心用 BGProcessingTask 设置
requiresExternalPower = true(仅充电时执行)。 - 减少 Background Fetch 触发频率,只用于"必须接近实时"的场景。
教训:iOS 的后台 API 越精细,误用代价越大。
# 18.3 国内 ROM 杀进程案例
某社交 App 在小米/华为设备上消息延迟严重,但 iPhone 和海外 Android 都正常。
根因:国内 ROM 后台清理激进——应用切后台 5 分钟就被杀。自建长连接断了,消息丢。
修法:
- 主推送走厂商推送融合(小米/华为 push SDK)。
- 自建长连接仅作为补充。
- 引导用户加白名单(保活无奈之举)。
教训:国内 Android 是"特殊战场"——FCM 不可用,必须按厂商分别接入。这不是技术问题而是生态问题。
# 18.4 案例的统一启示
- 重试必须退避:定时重试是电池杀手。
- iOS API 越精细越要慎用:默认值通常最优。
- 国内 Android 必须接厂商推送:FCM 不可用是政治现实,不可绕过。
- 物理台是侦探的放大镜:没有物理台数据,所有"我觉得"都不可信。
# 19.防劣化体系
# 19.1 三道防线总览
第一道·开发期 ─▶ 第二道·CI ─▶ 第三道·线上
Lint + Profiler 物理台基线 batterystats / MetricKit
阻断"坏模式" 新版本 > 基线 抽样上报
2
3
# 19.2 编码期 Lint
| Lint 规则 | 作用 |
|---|---|
WakelockTimeout | WakeLock.acquire 必须带 timeout |
BatteryLife | 检测 setExactAndAllowWhileIdle 滥用 |
LocationSuppliesPermission | 提醒 GPS 高精度的功耗代价 |
UnsafeBackgroundJob | Service / IntentService 长任务警告 |
# 19.3 CI 与 SLO
CI 自动化:
- 每次构建跑物理台基线测试(自动化设备 + Monsoon)。
- 关键场景:启动 1 分钟 / 后台 1 小时 / 导航 30 分钟。
- 新版本超基线 10% 阻断合并。
线上 SLO 示例:
| 指标 | 阈值 |
|---|---|
| 后台每小时耗电 | ≤ 0.5%(低端机) |
| 唤醒次数 | ≤ 6 次/小时 |
| 后台 CPU 时长占比 | ≤ 1% |
| WakeLock 累计时长 | ≤ 30 秒/小时 |
| 应用商店"耗电"差评率 | < 5% |
# 19.4 度量数据闭环
线上 batterystats / MetricKit
↓
异常机型 / 异常版本告警
↓
定位到具体 SDK / 功能
↓
修复 + 回归 CI 物理台
↓
灰度验证 → 全量发布
2
3
4
5
6
7
8
9
# 20.跨平台速查
# 20.1 工具速查
| 平台 | 物理台 | 系统统计 | 线上监控 |
|---|---|---|---|
| Android | Monsoon Power Monitor | Battery Historian | batterystats + 自实现 |
| iOS | Monsoon + Energy Log | MetricKit Energy | MetricKit + 自实现 |
| Web | N/A | Performance API(间接) | Beacon |
# 20.2 关键 API 速查
| 场景 | Android | iOS | Web |
|---|---|---|---|
| 后台调度 | WorkManager | BGTaskScheduler | requestIdleCallback |
| 定位(省电) | FusedLocationProvider PRIORITY_LOW_POWER | startMonitoringSignificantLocationChanges | watchPosition + maximumAge |
| 推送 | FCM / 厂商 push | APNs | Web Push |
| 检测低电量 | PowerManager.isPowerSaveMode | ProcessInfo.isLowPowerModeEnabled | navigator.getBattery(已废) |
| 检测网络 | ConnectivityManager | NWPathMonitor | navigator.connection |
| 检测设备空闲 | PowerManager.isDeviceIdleMode | UIApplication.isIdleTimerDisabled | document.hidden |
# 20.3 通用 SLO 速查
| 指标 | 推荐值 |
|---|---|
| 心跳间隔 | 5-10 分钟 |
| 后台唤醒次数 | < 6 次/小时 |
| 后台 CPU 占比 | < 1% |
| 前台 1 小时耗电 | < 10% |
| GPS 持续时长 | < 5 分钟(除导航场景) |
| WakeLock 单次最长 | < 10 分钟 |
# 21.总结与延伸
# 21.1 一图概括
功耗优化
├─ 屏幕 → OLED 暗黑 + 亮度自动 + 灭屏快
├─ CPU → 任务批量 + 让系统调度
├─ 通信 → 心跳 5+ 分钟 + 批量打包
├─ 定位 → 被动 > 网络 > GPS + 传感器融合
└─ 监控 → 唤醒次数 + WakeLock 时长
2
3
4
5
6
# 21.2 五条核心原则
- 让硬件多睡觉:CPU 降频、屏幕灭、网卡休眠是终极手段(§03.3 三态模型)。
- 批量 > 频繁:§17.3 批量省 88%。
- 被动 > 主动:§17.2 定位 GPS vs 被动 70× 差。
- 让系统决策:WorkManager + Constraints / BGTaskScheduler 是最佳实践(§15 三层让)。
- 按硬件适配:§17.4 OLED/LCD 差异化。
# 21.3 五个常见误区
- "降亮度治耗电":错(§02 案例 屏幕不是主战场)。
- "心跳越长越省电":错(10 分钟后边际为零,失去保活)。
- "GPS 精度高就好":错(§17.2 70× 功耗差)。
- "5G 永远更好":错(§17.5 小数据反而费电)。
- "绕过 Doze 能省":错(被系统降权,长期得不偿失)。
# 21.4 三个外延
- 碳排追踪:手机功耗最终对应碳排放,是 ESG 的端侧指标——大型应用未来必须给出"碳排报告"。
- 设备老化:电池循环 500+ 次后容量衰减,App 应自适应低电量行为——读取
BatteryManager.BATTERY_PROPERTY_CAPACITY健康度。 - AI 调度:未来 OS 可能用 ML 预测 App 唤醒时机,进一步省电——Android 12+ App Standby Buckets 已是雏形。
# 21.5 给团队的建议
- 第 1 周:审计所有后台任务,无必要的全部砍掉(§13 一层)。
- 第 2 周:心跳间隔统一调到 5 分钟以上,所有非紧急请求批量化(§14 二层)。
- 第 3 周:定位精度全面降级,传感器融合(除非业务必须)(§16 四层)。
- 第 4 周:上线 batterystats / MetricKit 抽样,建电量大盘(§19 防劣化)。
# 一句话总结
功耗是"看不见的承诺"——用户不会因为电池长 1 小时来夸你,但一定会因为电池短 1 小时骂你。 让硬件多睡觉 = 少 + 批 + 让 + 轻四字诀;定位 + 通信 + 屏幕是三大主战场。 §02 案例 那个"3 周经验派 28% 耗电 vs 5 天方法派 11% 耗电"的反差,正是这条路径的最锋利证据。