1 耗电原因
- app消耗电量的体现,为以下各模块的耗电
屏幕、CPU、相机、闪光灯、音频、视频、蓝牙、modem、wifi、gps
- 单位耗电量排序
相机(平均1152mAh) > modem(最高604mAh) > wifi(发数据370mAh) > CPU(最高频率290mAh) > 屏幕(最亮240mAh) > 音频(75mAh) > 视频(50mAh) > GPS(49mAh) > 蓝牙(8mAh)
2 日志内容
- 导出日志的命令
adb shell dumpsys batterystats > batterystats.txt
- 日志片段
Battery History (2% used, 11KB used of 512KB, 102 strings using 9638):
0 (10) RESET:TIME: 2019-02-18-15-49-33
0 (1) 034 status=charging health=good plug=usb temp=330 volt=3768 charge=948 +running +wake_lock +mobile_radio +screen +plugged data_conn=lte phone_signal_strength=excellent brightness=medium +wifi_running +wifi +usb_data wifi_signal_strength=4 wifi_suppl=disconn gps_signal_quality=good
0 (2) 034 proc=1000:"com.xiaomi.joyose"
0 (2) 034 proc=1000:"com.goodix.fingerprint"
0 (2) 034 proc=u0a150:"com.tencent.mobileqq"
0 (2) 034 proc=u0a211:"com.miui.systemAdSolution"
0 (2) 034 proc=1001:"com.android.phone"
0 (2) 034 proc=1000:"com.miui.contentcatcher"
0 (2) 034 proc=u0a196:"com.sohu.inputmethod.sogou"
0 (2) 034 proc=u0a3:"com.lbe.security.miui"
0 (2) 034 proc=1000:"com.miui.wmsvc"
-
整个日志的结构大致如下
* Battery History: 耗电统计的历史记录,每一条记录以HistoryItem的形* 式存在
* Per-PID Stats: 每个进程唤醒工作的时间
* Discharge step durations: 每掉一隔电的时间点和设备的状态
* Daily stats: 以天为单位展示每掉一隔电的时间点和设备状态
* Statistics since last charge: 从上次充电以来的统计详情,包含很多子板块
* Cellular Statistics: 移动数据网络状态和使用情况
* Wifi Statistics: WIFI的网络状态和使用情况
* Bluetooth: 蓝牙在不同工作状态下的使用情况
* Estimated power use (mAh): 近似计算出的各个用户(uid)的耗电量,一个APK通常对应到一个用户,当然,也有多个APK共享一个用户的情况
2-1 Battery History
batterystats会被动(push)或主动(pull)的获取电量统计信息,每一次都会将获取到的信息记录下来,每一条信息会以HistoryItem对象的形式保存在一个缓冲区。整个Battery History的日志结构如下图所示:
HistoryItem可以理解为一次记录耗电变化的动作,因此,它被设计为命令的方式,每生成一个HistoryItem,就相当于发起了一次耗电统计的命令。HistoryItem详细的描述了在某一个时刻,影响耗电的各因素所在的状态,是分析功耗问题重要的依据。随着待机时间的延长,HistoryItem对象的数量就越多,因此,整个电量统计日志可能很大篇幅都被Battery History占据了,下面就详细地剖析其中意义。
- 第一行
Battery History (2% used, 11KB used of 512KB, 102 strings using 9638):
用于存储HistoryItem的缓存并非无限大,Android默认为512KB,此处使用了11KB,所以使用率为2%。另外,因为日志中频繁出现的也是一些固定的字符串,为了节省空间,统计信息都是以极简的格式存储,所以,此处还有一个字符串的缓存。
- 第二行
0 (10) RESET:TIME: 2019-02-18-15-49-33
这是第一条HistoryItem记录: RESET命令。RESET表示重新开始一个时间段的电量统计。为什么需要RESET呢? 因为在统计之前可能是充电状态, 停止充电后才有统计的必要。
- 0: 表示当前开始统计的起点时间
- (10): 从缓存中读取的子节数
- RESET命令,该处可能出现的另外命令还有START(开机时的统计记录),SHUTDOWN(关机时的统计记录),OVERFLOW(缓冲区溢出的记录)。 RESET和START命令会打印出当前的时间,即TIME: 2019-02-18-15-49-33
- 第三行
0 (1) 034 status=charging health=good plug=usb temp=330 volt=3768 charge=948 +running +wake_lock +mobile_radio +screen +plugged data_conn=lte phone_signal_strength=excellent brightness=medium +wifi_running +wifi +usb_data wifi_signal_strength=4 wifi_suppl=disconn gps_signal_quality=good
- 100 当前电量百分值,随着时间的推移,这个值会逐渐减少
- status: 充电状态(unknown未知状态,charging充电状态,discharging放电状态,not-charging没有充电,full已充满)
- health: 电池状态(unknown未知状态,good正常,overheat过热,dead已坏,over-voltage电压过高,cold过冷)
- plug: 插拔连接状态(none未插拔,ac交流电,usb数据线,wireless无线连接)
- temp: 电池温度
- volt: 电池电压
- state: 主动记录的影响耗电的硬件工作状态,这些状态从字面意思就能理解
- 部分状态前面会带上+或者-,+表示进入状态,-表示离开状态,譬如: +running表示CPU进入运行状态,-wakelock表示释放锁
- 部分状态设置了状态值。譬如: phone_signal_strength=great表示当前手机信号很强
- wakeup_reason: 如果存在,则会打印唤醒的理由
- event: 被动通知的影响耗电的事件,event会带上事件源的uid
- 部分事件前面会带上+或者-,+表示进入事件,-表示离开事件,譬如: +top=u0a70:”com.test.mygame”表示当前进入显示的界面为com.test.mygame,事件源的uid为u0a70
- 所有事件都有一个描述值,即=右边的内容
- 第四、五行
0 (2) 034 proc=1000:"com.xiaomi.joyose"
0 (2) 034 proc=1000:"com.goodix.fingerprint"
这是第三条和第四条HistoryItem记录,分别记录了两个事件(event),Android系统进程启动完成时会通知batterystats
- 第六、七行。。。
0 (2) 100 alarm=u0a165:"*walarm*:com.alipay.mobile.healthcommon.broadcast.TIME_TO_COUNT_STEP"
+3ms (2) 100 stats=0:"screen-state"
+10ms (2) 100 +camera +wake_lock_in=1000:"*vibrator*"
+17ms (2) 100 +alarm=1000:"*alarm*:android.intent.action.TIME_TICK"
+19ms (2) 100 +wake_lock_in=1000:"*alarm*:android.intent.action.TIME_TICK"
+21ms (2) 100 +wake_lock_in=1001:"*telephony-radio*"
+23ms (2) 100 +wake_lock_in=u0a140:"*telephony-radio*"
+23ms (2) 100 +wake_lock_in=u0a149:"*telephony-radio*"
+24ms (2) 100 -wake_lock_in=1001:"*telephony-radio*"
+27ms (2) 100 -wake_lock_in=u0a140:"*telephony-radio*"
+27ms (2) 100 +wifi_scan -wake_lock_in=u0a149:"*telephony-radio*"
+52ms (2) 100 +wake_lock_in=1027:"NfcService:mRoutingWakeLock"
+73ms (3) 100 +audio -wake_lock_in=1000:"*vibrator*"
......
剩下的HistoryItem记录,用时间序记录了耗电过程,整个过程其实就是硬件状态和耗电事件交替发生变化。随着时间的推移,系统在睡眠中会被唤醒,CPU开始运转,camera唤醒,wifi扫描,这些都直接反映到了上述日志中。
2-2 Per-PID Stats
每个进程wakelock的持锁时间
Per-PID Stats:
PID 0 wake time: +2s728ms
PID 1333 wake time: +8s574ms
PID 1923 wake time: 0
PID 1333 wake time: +6ms
PID 2088 wake time: +50ms
PID 2096 wake time: +7s369ms
PID 3769 wake time: +87ms
PID 0 wake time: +12s857ms
PID 1333 wake time: +2ms
日志中会看到一些重复的PID,因为这份日志是按照uid来归类的(但并没有把uid打印出来),表达的是一个uid关联到的所有进程持有wakelock的时间。
2-3 Discharge step durations:
每隔电的掉电时间和设备状态
Discharge step durations:
#0: +1m42s403ms to 97 (screen-on, power-save-off, device-idle-off)
#1: +1m21s919ms to 98 (screen-on, power-save-off, device-idle-off)\
第#0条记录,表示用了1分42秒的时间,电量从98%已经掉到97%,手机此时的状态是:不在省电状态(power-save-off),也不在空闲状态(device-idle-off)。 当然,在掉电的过程中,手机还可能处于其他状态:
- screen-on/scree-off:屏幕是否点亮。如果我们发现日志中,screen-on这个状态高频出现,那说明手机处于比较耗电的状态中。
- power-save-on/power-save-off:是否开启省电模式。在设置中,可以打开手机进入省电模式的开关,在省电状态下,手机会降低运行性能、禁止一些后台服务(譬如收邮件、定位等)。默认情况下,该开关是关闭的,既手机处于power-save-off的状态。
- device-idle-on/device-idle-off:是否处于空闲状态。从Android M(6.0)开始,就引入了Doze模式,简单来说,就是手机满足一定的条件(灭屏、静止、没有充电)时,就会进入到一种休眠状态(IDLE),在这种状态下,所有CPU、网络、外设的使用请求都会被搁置。在深度休眠一段时间后,手机又会被唤醒,留出一小段窗口期(IDLE_MAINTAINESS),执行之前搁置的请求。
2-4 Daily stats
以天为单位,展示每隔电的掉电时间和设备状态。这一部分日志的含义与上面相同,仅仅加了一个分类,不再赘述。
2-5 Statistics since last charge
自从上次充电以来的电量统计,既最近一次拔下充电线,仅仅使用手机电池的统计。这一部分日志是重点,包含很多方面的信息。
我们先来看整个耗电的统计概览:
Statistics since last charge:
System starts: 0, currently on battery: false
Estimated battery capacity: 2679 mAh
Min learned battery capacity: 2863 mAh
Max learned battery capacity: 2863 mAh
Time on battery: 5m 45s 235ms (99.2%) realtime, 5m 45s 235ms (100.0%) uptime
Time on battery screen off: 0ms (0.0%) realtime, 0ms (0.0%) uptime
Time on battery screen doze: 0ms (0.0%)
Total run time: 5m 47s 923ms realtime, 5m 47s 922ms uptime
Discharge: 62.7 mAh
Screen off discharge: 0 mAh
Screen doze discharge: 0 mAh
Screen on discharge: 62.7 mAh
Device light doze discharge: 0 mAh
Device deep doze discharge: 0 mAh
Start clock time: 2019-02-19-15-51-04
Screen on: 5m 45s 235ms (100.0%) 0x, Interactive: 5m 45s 235ms (100.0%)
Screen brightnesses:
dim 5m 45s 235ms (100.0%)
Connectivity changes: 3
Total full wakelock time: 2s 272ms
- System starts:手机重启次数,currently on battery:当前是否正在使用电池
- Estimated battery capacity:电池总容量
- Time on battery:电池使用时间。这个有两个时间:
- realtime:正常流逝的时间,通常把这个时间叫做“墙上时间(walltime)”,就像挂在墙上的时钟一样,走过了一小时就是小时
- uptime:CPU工作的时间。“墙上时间”经过一小时,但CPU可能就工作了一分钟,其他时间CPU都在休眠
这两个时间结合在一起,就能够知道休眠率: (realtime-uptime)/realtime,既手机休眠的时间占整个待机时间的比率。休眠率越高,就表明越省电。
- Time on battery screen off:在灭屏状态下,电池的使用时间。通常,在灭屏时,我们希望手机可以快速进入休眠状态,所以,这里计算出的休眠率使我们考察耗电问题的重要指标
- Time on battery screen doze:在Doze状态下,电池的使用时间
- Total run time:总共的电池使用时间
- Discharge:放电量,既总共使用的电量
- Screen off discharge:灭屏状态下的放电量
- Screen doze discharge:Doze状态下的放电量
- Screen on discharge:亮屏状态下的放电量
- Start clock time:开始电量统计的时刻
- Screen brightnesses:屏幕在不同亮度下的时间
- Total partial wakelock time:应用层持有wakelock的总时间
2-5-1 Estimated power use
计算得到,不同硬件和应用的耗电情况排行
Estimated power use (mAh):
Capacity: 3400, Computed drain: 44.9, actual drain: 68.0-102
Unaccounted: 23.1 ( ) Including smearing: 0 ( ) Excluded from smearing
Screen: 19.6 Excluded from smearing
Uid u0a124: 16.0 ( cpu=4.38 radio=11.6 wifi=0.00580 gps=0.00936 sensor=0.000149 ) Including smearing: 18.0 ( proportional=1.94 )
Uid 1000: 4.02 ( cpu=3.85 radio=0.00146 wifi=0.00575 sensor=0.0000303 camera=0.158 ) Excluded from smearing
Uid 0: 2.02 ( cpu=1.81 radio=0.206 wifi=0.00166 ) Excluded from smearing
Idle: 0.467 Excluded from smearing
Uid u0a18: 0.304 ( cpu=0.113 radio=0.191 ) Including smearing: 0.341 ( proportional=0.0369 )
Uid 1052: 0.300 ( cpu=0.00173 radio=0.299 ) Excluded from smearing
Wifi: 0.299 ( cpu=0.0266 wifi=0.272 ) Including smearing: 0.335 ( proportional=0.0363 )
Cell standby: 0.291 ( radio=0.291 ) Excluded from smearing
Uid 1051: 0.256 ( radio=0.256 ) Excluded from smearing
- Capacity: 电池容量, Computed drain: 计算得到的耗电量, actual drain: 实际耗电量。本例中,计算得到的耗电量(44.9mAh)竟然是实际耗电量(68.0~102mAh)的10几倍,进一步说明耗电统计只是一个近似计算
- Cell standby:射频待机的耗电量。正是由于这里计算出了26122mAh的耗电量,导致总体计算值出现了很大的偏差。为什么会计算出这么大的值呢?要么是单位电流值配置出错了,要么是使用时间计算出错了
- Bluetooth:蓝牙使用的耗电量。
- Uid XXX:一个Uid的耗电量,可以近似理解为一个应用的耗电量。在高版本的日志中,会再进行细分,括号中cpu=4.38 radio=11.6 wifi=0.00580 gps=0.00936 sensor=0.000149,就表明一个应用使用cpu的电量是4.38mA,使用传感器的耗电量是0.000149mAh等
这一部分日志是按照耗电量的从大到小排序的,通常我们只需关注头部耗电占比严重的模块