电能 = 电压 * 电流 * 时长 模块电量(mAh) = 模块电流(mA) * 时长(h)
系统电量优化红线规则
华为电量优化红线规则
Android 系统的电量统计工作,是由一个叫 BatteryStatsService 的系统服务完成的。
其中四个比较关键的角色:
Android 系统要求不同的厂商必须在 /frameworks/base/core/res/res/xml/power_profile.xml 中提供组件的电源配置文件。
从手机中导出 /system/framework/framework-res.apk 文件。 使用反编译工具(如 apktool)对导出文件 framework-res.apk 进行反编译。 查看 power_profile.xml 文件在 framework-res 反编译目录路径:/res/xml/power_profile.xml。
<?xml version="1.0" encoding="utf-8"?>
<device name="Android">
<!-- 显示相关配置 -->
<!-- 环境模式的平均电流消耗,包含背光 -->
<!-- 环境模式主要应用在 TV 上,类似电脑中的屏幕保护。 -->
<item name="ambient.on.display0">0.1</item> <!-- ~100mA -->
<!-- 环境模式的平均电流消耗,不包含背光 -->
<item name="screen.on.display0">0.1</item> <!-- ~100mA -->
<!-- 全亮度时背光的平均电流消耗 -->
<item name="screen.full.display0">0.1</item> <!-- ~100mA -->
<!-- 蓝牙数据传输 -->
<item name="bluetooth.active">0.1</item> <!-- ~10mA -->
<!-- 蓝牙开启并可连接,但未连接状态 -->
<item name="bluetooth.on">0.1</item> <!-- ~0.1mA -->
<!-- WIFI 开启 -->
<item name="wifi.on">0.1</item> <!-- ~3mA -->
<!-- WIFI 数据传输 -->
<item name="wifi.active">0.1</item> <!-- ~200mA -->
<!-- WIFI 扫描 -->
<item name="wifi.scan">0.1</item> <!-- ~100mA -->
<!-- 音频 -->
<item name="audio">0.1</item> <!-- ~10mA -->
<!-- 视频 -->
<item name="video">0.1</item> <!-- ~50mA -->
<!-- 相机闪光灯 -->
<item name="camera.flashlight">0.1</item> <!-- ~160mA -->
<!-- 相机平均值 -->
<item name="camera.avg">0.1</item> <!-- ~550mA -->
<!-- GPS 开启 -->
<item name="gps.on">0.1</item> <!-- ~50mA -->
<!-- 移动通信模块相关值。对于固件中没有能源报告支持的调制解调器,请使用 radio.active、radio.scanning 和 radio.on。 -->
<!-- 移动通信模块传输 -->
<item name="radio.active">0.1</item> <!-- ~200mA -->
<!-- 移动通信模块信号扫描 -->
<item name="radio.scanning">0.1</item> <!-- ~10mA -->
<!-- 移动通信模块传输时不同信号强度下的电流消耗 -->
<array name="radio.on"> <!-- 信号强度 0 到 BINS-1 -->
<value>0.2</value> <!-- ~2mA -->
<value>0.1</value> <!-- ~1mA -->
</array>
<!--CPU 运行时的额外功耗(不包括集群和核心)-->
<array name="cpu.active">
<value>0.1</value>
</array>
<!-- 每个 CPU 集群的核心数 -->
<!-- CPU 集群就是常说的 CPU 大小核。
例如下面就列举了,有两个 CPU 集群(暂时可以理解为 大核+小核,4个大核,2个小核)。
<array name="cpu.clusters.cores">
<value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3
<value>2</value> // cluster 1 has cpu4, cpu5
</array>
-->
<array name="cpu.clusters.cores">
<value>1</value> <!-- cluster 0 has cpu0 -->
</array>
<!-- 不同 CPU 集群的运行速率-->
<array name="cpu.speeds.cluster0">
<value>400000</value> <!-- 400 MHz CPU speed -->
</array>
<!--- CPU 集群运行时电流消耗 -->
<array name="cpu.active.cluster0">
<value>0.1</value> <!-- ~100mA -->
</array>
<!-- CPU 空闲时电流消耗 -->
<item name="cpu.idle">0.1</item>
<!-- 内存组件(内存总线、内存控制器、RAM 等)电流消耗 -->
<array name="memory.bandwidths">
<value>22.7</value> <!-- mA for bucket: 100mb/s-1.5 GB/s memory bandwidth -->
</array>
<!-- 标压下电池容量,单位 mAh -->
<item name="battery.capacity">1000</item>
<!-- WIFI 相关配置 -->
<!-- WIFI 控制器(负责管理 Wi-Fi 连接、数据传输和接收等操作)空闲时电流消耗,默认为 0 -->
<item name="wifi.controller.idle">0</item>
<!-- WIFI 控制器接收时电流消耗,默认为 0 -->
<item name="wifi.controller.rx">0</item>
<!-- WIFI 控制器传输时电流消耗,默认为 0 -->
<item name="wifi.controller.tx">0</item>
<!-- WIFI 控制器在不同传输功率下的电流消耗,默认为空 -->
<!-- 不同传输功率通常与 WIFI 信号强度、距离有关 -->
<array name="wifi.controller.tx_levels"> <!-- mA -->
</array>
<!-- WIFI 控制器工作电压,单位 mV,默认为 0 -->
<item name="wifi.controller.voltage">0</item>
<!-- WIFI 控制器在批量扫描时的电流消耗 -->
<array name="wifi.batchedscan"> <!-- mA -->
<value>.0002</value> <!-- 1-8/hr -->
<value>.002</value> <!-- 9-64/hr -->
<value>.02</value> <!-- 65-512/hr -->
<value>.2</value> <!-- 513-4,096/hr -->
<value>2</value> <!-- 4097-/hr -->
</array>
<!-- 移动通信模块(如蜂窝网络、LTE、5G)相关配置-->
<modem>
<!-- 睡眠状态电流消耗,单位 mA -->
<sleep>0</sleep>
<!-- 空闲状态电流消耗,单位 mA -->
<idle>0</idle>
<!-- 可根据不用模式指定多个 <active> 标签 -->
<!-- 可用属性:
rat: 指定移动通信技术:LTE、NR、DEFAULT
nrFrequency: NR 处于活动状态时频率级别:"LOW"、"MID"、"HIGH"、"MMWAVE"、"DEFAULT",
LOW 表示 <1GHz
MID 表示 1GHz - 3GHz
HIGH 表示 3GHz - 6GHz
MMWAVE 表示 >6GHz
-->
<active rat="DEFAULT">
<!-- 接收时电流消耗,单位 mA -->
<receive>0</receive>
<!-- 传输时电流消耗,单位 mA。必须定义所有级别(0 到 4) -->
<transmit level="0">0</transmit>
<transmit level="1">0</transmit>
<transmit level="2">0</transmit>
<transmit level="3">0</transmit>
<transmit level="4">0</transmit>
</active>
</modem>
<!-- 移动通信模块(调制解调器)工作电压,单位 mV -->
<item name="modem.controller.voltage">0</item>
<!-- GPS 相关配置 -->
<!-- GPS 信号强度 -->
<array name="gps.signalqualitybased"> <!-- 强度 0 to 1 -->
<value>0</value>
<value>0</value>
</array>
<!-- GPS 工作电压,单位 mV -->
<item name="gps.voltage">0</item>
</device>
BatteryStatsService 持有 BatteryStats 类,BatteryStats 又持有一个 Uid [] 数组,每一个 Uid 对应一个 App,安装或者卸载 App ,BatteryStats 会更新相应的 Uid 元素的映射关系。同时 BatteryStats 持有一系列的 StopWatch 和 SamplingCounter,当 App 开始使用某些硬件模块的功能时,BatteryStats 就会调用相应 Uid 的 StopWatch 或 SamplingCounter 来统计其硬件使用时长。
以 Wifi 模块来举例:
BatteryStats 是通过依赖的一个 BatteryStatsHelper 的辅助类来完成的。BatteryStatsHelper 通过组合使用 Uid 里的时长数据、PoweProfile 里的功率数据(power_profile.xml 的解析实例)以及具体各个模块的 PowerCalculator 算法,计算出每一个 App 的综合电量消耗,并把计算结果保存在 BatterySipper [] 数组里(按计算值从大到小排序)。
以 Wifi 模块来举例:
工具 | 优点 | 缺点 |
---|---|---|
手机耗电排行 | 直观 | 没有详细数据 |
监听电池状态 | 可知电池状态、电量 | 只是整体耗电情况、非实时 |
dumpsys batterystats | 可知App整体耗电量、各功能模块耗电量 | 可读性比较差、非实时 |
Battery Historian | 结果直观、有耗电量详情 | 适应 Android 5.0 及以上系统、无堆栈信息、非实时 |
BatteryCanary | 实时监测、支持问题堆栈 |
获取充电状态、电池电量 监控电池电量和充电状态
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryState = registerReceiver(null, intentFilter);
int chargeType = batteryState.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
Log.e("qingshan","充电方式 = " + chargeType); //1.交流电;2.USB;4.无线充电
int batteryPower = batteryState.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
Log.e("qingshan","电量 = " + batteryPower);
通过 dumpsys batterystats 导出。
$ adb shell dumpsys batterystats your.package.name > battery.txt
# 各个Uid的总耗电量,而且是粗略的电量计算估计。
# Estimated power use (mAh):
# Capacity: 3450, Computed drain: 501, actual drain: 552-587
# ...
# Idle: 41.8
# Uid 0: 135 ( cpu=103 wake=31.5 wifi=0.346 )
# Uid u0a208: 17.8 ( cpu=17.7 wake=0.00460 wifi=0.0901 )
# Uid u0a65: 17.5 ( cpu=12.7 wake=4.11 wifi=0.436 gps=0.309 )
# ...
# reset电量统计
$ adb shell dumpsys batterystats --reset
docker run -p <port>:9999 gcr.io/android-battery-historian/stable:3.0
# 重置电量信息
adb shell dumpsys batterystats --reset
# 开始记录电量
adb shell dumpsys batterystats --enable full-wake-history
# 导出电量信息
$ adb bugreport bugreport.zip # 7.0和7.0以后
$ adb bugreport > bugreport.txt # 6.0和6.0之前:
打开 http://localhost:<port>,上传 bugreport.zip,等待分析结果
实战:通过场景前后对比耗电差异 -> 大众点评App的短视频耗电量优化实战
Matrix-Battery Canary-Demo参考配置
重点模块:CPU、屏幕、WIFI、数据网络、GPS、音视频通话
合理使用后台任务框架。
后台工作概览 | Background work | Android Developers
Hi,我是“青杉”,您可以通过如下方式关注我:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。