Android App 耗电的测试方法
这是一篇讲述应用耗电的文章,围绕 Android 电量采集机制及第二代Battery Historian分析工具讲述。文从数据采集、导出、环境搭建、解读报告的角度出发,从细节讲解整个流程
电量统计模块概述
Android 从两个层面统计电量的消耗,分别为软件排行榜及硬件排行榜。它们各有自己的耗电榜单,软件排行榜为机器中每个 App 的耗电榜单,硬件排行榜则为各个硬件的耗电榜单。这两个排行榜的统计是互为独立,互不干扰的。
** 此处主要讲述软件层面的统计。**
具体的说,耗电信息在设置 -> 电量中能够非常直观的看到。注意,Android 所有功耗统计都是通过代码估算,没有集成电路参与汇报。准确度取决于厂商 ROM 所提供的power_profile.xml文件。由于不同厂商power_profile.xml准确度及源码有差异,因此不同手机、不同版本的数据可能有较大差异。
power_profile.xml直接影响统计的准确度,并且此文件无法通过应用修改。再次强调,Android 耗电估算没有硬件的参与,全靠代码估算。
power_profile.xml文件位于源码下的/framework/base/core/res/res/xml/power_profile.xml,部分内容展示如下:
<itemname="radio.scanning">0.1</item><!-- cellular radio scanning for signal, ~10mA --><itemname="gps.on">0.1</item><!-- ~50mA --><!-- Current consumed by the radio at different signal strengths, when paging --><arrayname="radio.on"><!-- Strength 0 to BINS-1 --><value>0.2</value><!-- ~2mA --><value>0.1</value><!-- ~1mA --></array></array><!-- Different CPU speeds as reported in
/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state --><arrayname="cpu.speeds"><value>400000</value><!-- 400 MHz CPU speed --></array><!-- Current when CPU is idle --><itemname="cpu.idle">0.1</item><!-- Current at each CPU speed, as per 'cpu.speeds' --><arrayname="cpu.active"><value>0.1</value><!-- ~100mA --></array><arrayname="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>
这就是在硬件层面统计时,直接参与运算的参数。无论是软件耗电统计还是硬件耗电统计,都通过BatteryStatsHelper来进行汇总。BatteryStatsHelper位于/framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java下。
软件耗电统计
在BatteryStatsHelper.java中,有这么一个方法:

processAppUsage()方法中,一个应用的总功耗在这里体现出来了:
cpu
Wakelock(保持唤醒锁)
无线电(2G/3G/4G)
WIFI
蓝牙
传感器
相机
闪光灯
这些数据,将决定着你的应用在耗电排行榜中的位置,以及是否给予用户警告高耗电。这些警告对于应用来说可能是致命的,用户可能因此而卸载应用。
应用总功耗是上述八个统计值的和。这八个统计器同继承自PowerCalculator.java
具体来说,这八个耗电计算器的算法分别如下:

耗电统计概述就如上所述。总的来说并不复杂,通过聚合八种不同方式的消耗,来得出总的耗电量,并给予用户展示。
耗电数据的采集
数据的采集是机器单方面的行为,不需要依赖第三方的辅助,因为这是 Android 系统级的功能。但在这之前,需要做一些准备。请事先打开开发者模式。
-
将移动设备连接到计算机。
-
在终端窗口中,关闭正在运行的 adb 服务器。
adb kill-server -
重启 adb 并检查是否有已连接的设备。
adb devices系统应该会列出您的设备,类似于下面的示例输出。
![adb 设备的输出]()
如果您没有看到任何设备,请确保您的手机已连接,且 USB 调试功能已开启,然后终止并重启 adb。
4.USB 接入计算机。在终端中执行:
adb shell dumpsys batterystats --enable full-wake-history
默认情况下,唤醒(wake)数据是不会被采集的,因此我们需要将其启用。如果采集的不是全量 wake up 数据,在分析阶段则不能很好的观测数据。
5.随后终端执行:
adb shell dumpsys batterystats --reset
此命令会清空历史采集的信息。
6.最后,拔出 USB。
最后,拔出 USB。
最后,拔出 USB。
重要的事说三遍!
现在,耗电统计已经开始了。没错,耗电统计就是一直开着的,并且无法关闭:这是一个系统级别的功能。
为什么要拔出 USB?因为如果你一直插着 USB ,如果电充满了,你的数据会被清空的。Batterystats 只会记录最后一次充满电后的记录,因此强烈建议先把电充满,完成以上操作后,拔出 USB 电源。
7.根据选定场景,在app上进行操作。
接下来,就像日常使用手机一样,操作你想要统计的应用。耗电记录器会在后台统计整台机子所有的耗电情况。没错,不需要事先指定目标 App ,所有 App 都会被统计。这也说明,任何人都能够统计任何已安装的应用。因此,除了统计自家 App ,也能用于统计竞品。
8.导出耗电数据。
当你觉得操作得差不多了,连接到 USB,终端执行:
对于搭载 Android 7.0 及更高版本的设备:
adb bugreport [path/]bugreport.zip
对于搭载 Android 6.0 及更低版本的设备:
adb bugreport [path/]bugreport.txt
bugreport.txt就是记载着整台手机耗电信息的源数据。
bug 报告可能需要几分钟才能完成。在完成之前,请勿断开设备连接或取消该进程。
这些文件是系统使用可选的 path 参数在您指定的目录中创建的。如果您没有指定路径,系统将在您的主目录中创建这些文件
9.最后终端执行:
adb shell dumpsys batterystats --disable full-wake-history
不要忘了关闭全量记录唤醒。保持开启会造成性能问题,除非在电量收集阶段,否则建议保持关闭。
接下来,搭建分析环境:Battery Historian。
搭建 Battery Historian & 上传 bugreport
Github上有本地安装和使用方法:https://github.com/google/battery-historian/
逃课教程:直接使用网络搭建共享网站https://bathist.ef.lc/
使用 Battery Historian 图表查看数据
Battery Historian 图表会显示一段时间内与电源相关的事件。
当系统组件由于处于活动状态而正在消耗电池电量时,每行都会显示一个彩色条形段。该图表不会显示该组件使用的电量,仅表示相关应用处于活动状态。图表按类别进行整理,并以一个条形显示每个类别随时间的变化,如图表的 X 轴所示。

图 3. Battery Historian 图表示例
- 从下拉列表中添加其他指标。
- 将光标悬停在信息图标上可查看有关每个指标的更多信息,包括图表中使用的颜色对应的键。
- 将光标悬停在某个条形上可查看有关该指标的更多详细信息以及时间轴上特定点的电池统计信息。
其他 Batterystats 输出
您可以在 Battery Historian 图表下方统计信息部分的 batterystats.txt 文件中查看其他信息。

图 4. Battery Historian 的统计信息部分
1 System Stats 标签页包含系统范围的统计信息,例如电池信号电平和屏幕亮度。此信息可全面反映设备的具体情况。这对于确保没有任何外部事件影响您的测试尤为有用。
2 App Stats 标签页包含有关特定应用的信息。使用左侧“App Selection”窗格中的 Sort apps by 下拉列表(标示为 3)对应用列表进行排序。您可以使用下方的应用下拉列表(标示为 4)选择特定应用来查看统计信息。
鸟瞰 Battery Historian
再次强调,bugreport文件包含了整台手机运行状况,并非单一某个 app,因此查看图表时要特别注意,数据所展示的是当前选中的 app 数据还是全部 app 的叠加。
现在,我用微信作为分析目标。选中 com.tencent.mm。

现在,这张图表第一个坑爹的地方出现了。选中目标包名前后,图标数据会有些不一样的地方。选中前:
选中后:
注意到加粗的 Top app , Activity Manager proc , JobScheduler 了吗?这几个数据在选中后,图表数据会变为仅有当前选中 app 的数据,而其他数据仍然是整台机子的全量数据。一不小心,还能坑你很多次。此处是初次使用 Battery Historian 需要特别注意的地方。用鼠标指向图标,可粗略地观察数据的变化。
数据分析分为三个 Tables,分别是System Stats,History Stats,App Stats。System Stats 和 App Stats 是重点观测和分析对象。
System Stats 包含了机子整体概况,包括整台机子在这段期间消耗了多少电量,所有 app 使用 Wakelocks、JobScheduler、CPU、Wifi、传感器等等一切的所有情况。
接下来则是 App Stats,所有的优化都是为了此处的数据而努力。
读懂 App Stats
App Stats 所展示的都是所选定包名所产生的数据,不会受到外部因素的影响。
Misc Summary 部分概述了所选定 app 在收集阶段的活动概况:

如上所述,
电量消耗占用了总消耗的3.94%;
前台运行了 3 小时 34 分钟;
震动了 8 次,共 225 毫秒;
CPU 用户态时间 22 分 33 秒;
Alarm 唤醒 40 次。
来看一个具体的数据:查看 App Stats 下的 Wakelocks 数据区域:

注意,显示为 WakerLock:xxxxxxx 是混淆导致。您自己的开发包不会存在此情况。
你还记不记得 App 的耗电排行榜是如何计算的?上面这些数据都会影响文章开头所提到的耗能计算公式。
举个例子,假如你一直持有一个 WakePowerLock,但你什么都没干 —— 这时候其实是不会产生真正耗电的,对吧。但因为你持有一个 Lock,公式就是这么算的:wakeLockTime * wakeLockPower。即使你啥也没干,Android 系统也认为你在耗电,这时候就很吃亏了。
再来看看 Alarm 唤醒(App Stats 下的 Wakeup alarm info):

查看你自家的 app,可能会惊讶的发现有如此之多不必要的唤醒。Wakeup Alarm 和 Scheduled Job 可能被某些厂商用于检测频繁后台唤醒,并向用户展示该信息。
最后,再来看看 Sensor Use 部分:

看看您自家的应用是否有过多的传感器调用?如非必要,能复用上一次的 GPS 数据吗?这些所有的资源消耗,都会被算入能耗当中。当您的 app 在耗电榜上屡次得冠,就离卸载不远了。
通用分析思路
1、通过system stats 中screenon/off rate 平均亮灭屏耗电熟读可以初步确认亮屏或者灭屏耗电是否有异常。
2、通过选取电量值观察每格电量的消耗速度,确认耗电异常时间段和当前前台应用,网络状态,后台job等信息。
3、综合当前亮度,网络状态,后台job ,前台应用估算是否符合预期,确认是否当前配置环境问题,还是应用耗电异常。
五、常见场景案例
1. 充电慢问题:查看充电电流值,确认充电电流是否符合预期。查看充电过程是否有异常job在长时间执行,如果有异常job耗电也会降低充电电流。查看对应电池温度是否有高温现象,电池温度过高会限制充电电流。
2. 发热问题:通过batterystats查看当前发热情况,找到温度最高区间,综合当前网络,亮度,应用确认耗电情况是否符合预期。如耗电电流不大,但是温度确持续增加,大概率环境无法散热导致,比如太阳光直射,物品覆盖无法散热。
3. 亮屏耗电问题:当出现亮屏耗电时,先检查网络状态,亮度状态,是否高耗电应用。不同网络耗电排行5G>4G>wifi,高亮下耗电会急剧增加,游戏相机场景会耗电大。
如下场景是用户反馈白天耗电快场景,4G下,高亮,加上后台GPS长时间定位,引起亮屏使用耗电大。
4. 息频耗电问题:息屏场景,部分app会通过音频持锁来给自己保活。通过查看audio和wakelock状态可以确认此类问题。app频繁网络包唤醒系统(应用唤醒频次低于1min),同样也会引起耗电快。
如下场景就是用户反馈息屏耗电快场景,从batteryhistorian可以看到xfPlay,一直持有音频锁给自己保活,导致系统无法休眠从而息屏耗电。
5. 息屏异常耗电问题:所有信息都符合预期,但是还是耗电快,此种情况可能是有异常器件漏电问题。
如下场景息屏耗电场景,唤醒周期超过2min,休眠比良好,但是耗电依旧很大。有可能是器件漏电,需要专业功耗工程师进一步分析。
6. 电量追赶问题:当发现耗电电流超过理论值,并且无异常发热,大概率是出现了电量追赶问题。当计算出来的真实电量和实际电量有差异时,实际电量就会快速下降追赶至真实电量,表现就是30s或者60s一格电的速度去追赶(各家参数会有差异)。
如下息屏耗电场景,平均耗电电流5466ma,理论手机不会出现这么大电流(手机平均电流极限2A左右,如是真实耗电会伴随大量发热)。此时温度正常,大概率是出现了虚电,电量追赶问题,需要从kernel 日志进一步分析确认。

浙公网安备 33010602011771号