编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 性能优化实践

    • README
    • 公共方法论

    • 体系建设篇

    • 资源专项篇

    • 流水线专项

    • 业务专项篇

      • App冷启动优化
      • 网络性能分析优化
      • 图片性能解码优化
      • 列表与滚动性能
      • 功耗与电量优化
        • 01.阅读说明
        • 02.贯穿案例
          • 2.1 案例背景
          • 2.2 经验派的 3 周折腾(典型反面教材)
          • 2.3 方法派的 5 天闭环
          • 2.4 上线效果
          • 2.5 案例串联全文
        • 03.功耗本质定义
          • 3.1 电池物理本质
          • 3.2 功耗的硬件构成
          • 3.3 三态模型(不可商量)
          • 3.4 用户感知层定义
          • 3.5 8 个反直觉问题
        • 04.功耗数学原理
          • 4.1 功耗基本公式
          • 4.2 tail time 数学模型
          • 4.3 功耗占比定律
          • 4.4 race-to-idle 的成立条件
          • 4.5 跨平台同构原理
        • 05.度量与采集
          • 5.1 三类捕获方案
          • 5.2 三方案差异矩阵
          • 5.3 跨平台采集对照表
          • 5.4 数据可信度评估
        • 06.归因决策树
          • 6.1 功耗问题二分决策树
          • 6.2 后台耗电四大元凶
          • 6.3 唤醒次数 vs 唤醒时长
          • 6.4 长尾耗电归因
        • 07.屏幕全链路 ⭐
          • 7.1 屏幕功耗的物理根源
          • 7.2 亮度的非线性功耗
          • 7.3 高刷新率的功耗代价
          • 7.4 应用层可干预点
        • 08.CPU 全链路 ⭐
          • 8.1 CPU 功耗的物理根源
          • 8.2 big.LITTLE 架构原理
          • 8.3 DVFS 与 big.LITTLE 原理
          • 8.4 Doze 与 App Standby 原理
        • 09.通信全链路 ⭐
          • 9.1 通信功耗的物理根源
          • 9.2 RRC 状态机原理
          • 9.3 5G 的功耗陷阱
          • 9.4 长连接 vs push 的选择
        • 10.定位与传感器 ⭐
          • 10.1 定位的物理根源
          • 10.2 GPS 工作原理与冷启动代价
          • 10.3 传感器融合的工程妙处
          • 10.4 iOS Significant Location Change 的妙处
        • 11.后台调度全链路 ⭐
          • 11.1 Android Doze + WorkManager 原理
          • 11.2 iOS BGTask 与 Jetsam
          • 11.3 厂商 ROM 后台清理的工程现实
          • 11.4 Web 后台标签节流原理
        • 12.跨端功耗对照
          • 12.1 端到端流程对照表
          • 12.2 占比与可干预度对比
          • 12.3 统一启示
        • 13.治理一层少
          • 13.1 一层命题
          • 13.2 策略 1.1:后台任务清单审查
          • 13.3 策略 1.2:用 push 替代轮询
          • 13.4 策略 1.3:WakeLock 强制超时
          • 13.5 一层反思
        • 14.治理二层批
          • 14.1 二层命题
          • 14.2 策略 2.1:网络请求批量化
          • 14.3 策略 2.2:定位 + 传感器融合
          • 14.4 策略 2.3:AlarmManager.setInexactRepeating
          • 14.5 二层反思
        • 15.治理三层让
          • 15.1 三层命题
          • 15.2 策略 3.1:WorkManager + Constraints(Android)
          • 15.3 策略 3.2:Doze 模式配合
          • 15.4 策略 3.3:iOS BGProcessingTask + requiresExternalPower
          • 15.5 策略 3.4:低电量模式自适应
          • 15.6 三层反思
        • 16.治理四层轻
          • 16.1 四层命题
          • 16.2 策略 4.1:定位优先级(被动 > 网络 > GPS)
          • 16.3 策略 4.2:暗黑模式(OLED 设备)
          • 16.4 策略 4.3:网络选择(小数据 4G/WiFi、大数据 5G)
          • 16.5 策略 4.4:传感器精度自适应
          • 16.6 优先级判定(ROI)
          • 16.7 四层反思
        • 17.求证实验
          • 17.1 实验一:心跳间隔的甜区
          • 17.2 实验二:定位精度的代价
          • 17.3 实验三:批量唤醒的收益
          • 17.4 实验四:屏幕亮度 + OLED 暗黑模式的真实功耗
          • 17.5 实验五:5G vs 4G vs WiFi 的功耗对比
          • 17.6 五大实验启示
        • 18.实战案例
          • 18.1 跨端同构案例·心跳风暴
          • 18.2 平台特异案例·iOS Background Fetch 滥用
          • 18.3 国内 ROM 杀进程案例
          • 18.4 案例的统一启示
        • 19.防劣化体系
          • 19.1 三道防线总览
          • 19.2 编码期 Lint
          • 19.3 CI 与 SLO
          • 19.4 度量数据闭环
        • 20.跨平台速查
          • 20.1 工具速查
          • 20.2 关键 API 速查
          • 20.3 通用 SLO 速查
        • 21.总结与延伸
          • 21.1 一图概括
          • 21.2 五条核心原则
          • 21.3 五个常见误区
          • 21.4 三个外延
          • 21.5 给团队的建议
        • 一句话总结
    • 交付防御篇

  • 程序编程原理

  • 稳定性与可靠性

  • 工程化与运维

  • 方案设计思想

  • 专栏
  • 性能优化实践
  • 业务专项篇
杨充
2026-05-27
目录

功耗与电量优化

# 功耗与电量优化

📊 学习成本预估 | 难度:⭐⭐⭐⭐(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 决策树):

  • 前台运行 → 看哪类密集 → 定位(38%)→ §10 定位原理。
  • 后台同步 → 心跳过频 → 5s 重试 → §09 RRC 状态机原理。

Day 3-4(§13-§16 分层策略):

  • 第 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         │
   └──────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11

三态模型的两个工程含义:

  1. 真正省电的是 C 态——A/B 态都在耗电。让硬件尽快从 A→C,跳过 B 是不可能的(系统统一管理)。
  2. 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 个反直觉问题

带着这些问题阅读:

  1. 为什么 App 在前台用得多反而比后台耗电少(按比例)?
  2. 屏幕亮 1 秒和 CPU 跑 1 秒,哪个更耗电?
  3. WiFi 和 4G 哪个更省电?
  4. 为什么"心跳间隔越长越省电"不一定成立?
  5. AlarmManager 的"精确"和"不精确"差几十倍功耗?
  6. 后台定位为什么是电量黑洞?
  7. 为什么 push 推送比 App 自轮询省电?
  8. 暗黑模式真的能省电吗?

# 04.功耗数学原理

本章把功耗从"经验数字"还原到第一性原理,回答四个核心问题:功耗如何计算 / tail time 怎么把成本放大 / 占比定律为何决定优化优先级 / race-to-idle 何时成立。

# 4.1 功耗基本公式

   能耗 E = ∫ P(t) dt          (瓦时 / 焦耳)
   功率 P = U × I              (瓦 = 电压 × 电流)
   电池消耗 = E / U_battery   (mAh = 能耗 / 电池电压)
1
2
3

三个推论:

  1. 功耗是积分量,不是瞬时量——P=10W 跑 1s 和 P=1W 跑 10s 等价。减少时长和降低瞬时都是手段,但前者通常更有效。
  2. 不同硬件电压不同——CPU 1.0V、屏幕 4.0V、通信芯片 3.3V,同样电流下屏幕功耗最高。这是为什么屏幕占 30-50% 的物理根源。
  3. 电池是 mAh 而非 mWh——所以厂商常用"电流 mA"作为功耗代理指标。但严格说,应该用 mW 才对——这是后面 §17 实验 数据需要注意的细节。

# 4.2 tail time 数学模型

   一次任务实际成本 = T_active × P_active + T_tail × P_tail
                    │           │           │           │
                    任务时长   工作功率    保持时长     保持功率
1
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 倍以上
1
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 功耗占比定律

   总功耗 = Σ (各模块占比 × 各模块绝对功耗)
1

优化优先级 = 占比 × 可优化空间——这是 §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. 业务需求约束
1
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 类:

   ① 物理功耗台(地面真值)
   ② 系统电量统计(估算 + 易得)
   ③ 业务事件归因(线上可追踪)
1
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
1
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%(永远高耗)  ← 这就是高频心跳的本质
1
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%
1
2
3
4
5

两个工程含义:

  1. 从 100% 降到 50% 省 30%,但从 50% 降到 25% 仅省 30%——降亮度的边际收益递减。
  2. 应用强制降亮度损害用户体验且收益小——这是 §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
1
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
                │   │   │   │
                活跃因子 电容 电压 频率
1
2
3
4

三个推论:

  1. 频率翻倍 → 功耗翻 4-8 倍(因为电压也得提)。这就是 §04.4 race-to-idle 不总成立的根因。
  2. 静态功耗(漏电)随温度指数增长——手机过热时 leakage 暴涨,再叠加降频。
  3. 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 模型               基于负载阈值
                  考虑能耗最优                简单快速
1
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);
1
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
1
2
3
4
5
6
7

App Standby(应用休眠):

   未使用 N 天 → 进入 Standby Bucket
                ↓
   限制后台 Job/Alarm/网络
                ↓
   用户打开 → 退回 active
1
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 新增)  ← 中间态,更省电
1
2
3
4
5
6
7
8
9
10

关键事实:

  1. 从 IDLE → CONNECTED 需要 1-3s 握手——握手过程功耗很高。
  2. CONNECTED 状态保持 5-10s(tail time)——这段时间持续高耗。
  3. 应用任意一次发包都会触发完整路径——所以"批量"才能摊薄成本。

数学例子:心跳 30s 一次

   每次心跳实际占用通信芯片 = 1s 握手 + 100ms 发送 + 5s tail = ~6s
   1 小时心跳 120 次 → 占用 720s = 12 分钟高功耗
   通信芯片 24 小时占用 = 24 × 12 / 60 = 4.8 小时(20%)
1
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()
// 但实际网络选择由系统决定,应用只能"提示"
1
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. 持续跟踪:保持锁定(持续耗电)
1
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(高精度)
1
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
    }
}
1
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+)时唤醒应用
1
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 暂停     释放压抑的任务      继续暂停
1
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)
1
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)
// 系统在最经济时机调度(可能几小时后)
1
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');
1
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)
//    - 不必要 + 任意:砍
1
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();
}
1
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 等)
    }
}
1
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
1
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(...)
1
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
)
1
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)") }
1
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)
1
2
3
4
5
6
7
8
9
10
11
// iOS
if ProcessInfo.processInfo.isLowPowerModeEnabled {
    AnimationManager.shared.disableAll()
    NetworkManager.shared.minimizeBackgroundActivity()
}
1
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 全开
}
1
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 推荐
}
1
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
    }
}
1
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)
1
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 反而费电                   ─┘
1
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
   阻断"坏模式"       新版本 > 基线   抽样上报
1
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 物理台
          ↓
   灰度验证 → 全量发布
1
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 时长
1
2
3
4
5
6

# 21.2 五条核心原则

  1. 让硬件多睡觉:CPU 降频、屏幕灭、网卡休眠是终极手段(§03.3 三态模型)。
  2. 批量 > 频繁:§17.3 批量省 88%。
  3. 被动 > 主动:§17.2 定位 GPS vs 被动 70× 差。
  4. 让系统决策:WorkManager + Constraints / BGTaskScheduler 是最佳实践(§15 三层让)。
  5. 按硬件适配:§17.4 OLED/LCD 差异化。

# 21.3 五个常见误区

  1. "降亮度治耗电":错(§02 案例 屏幕不是主战场)。
  2. "心跳越长越省电":错(10 分钟后边际为零,失去保活)。
  3. "GPS 精度高就好":错(§17.2 70× 功耗差)。
  4. "5G 永远更好":错(§17.5 小数据反而费电)。
  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% 耗电"的反差,正是这条路径的最锋利证据。

上次更新: 2026/06/07, 10:26:12
列表与滚动性能
崩溃捕获设计实践

← 列表与滚动性能 崩溃捕获设计实践→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式