Android 功耗测试
功耗相关硬件
电池
- 电池容量:电池容量越大手机越笨重
- 充电时间:低电压高电流,高电压低电流
- 寿命
- 安全性
耗电硬件
- 处理器芯片:CPU,GPU,NPU
- 基带芯片:蜂窝网,WIFI,NFC
- 传感器:加速度传感器陀螺仪,气压计,温度传感器
- 外设:相机,麦克风,扬声器
- 其他:GPS,内存,显示屏,闪存
资源调度机制是厂商功耗优化的最重要的手段,手机基带,GPS,这些模块在不使用时也会进入低功耗或休眠模式,达到降低功耗的目的。
手机厂商为了保证头部应用能有更好的体验,厂商愿意给他们分配更多的资源
https://developer.qualcomm.com/software/snapdragon-power-optimization-sdk/quick-start-guide
软件
如何评估软件的耗电情况: 电能 = 电压 * 电流 * 时间
如何监测软件耗电:手机的电压一般恒定,Adnroid 系统要求可以在/frameworks/base/core/res/xml/power_profile.xml找到电源配置文件
如何在不同厂商中获取耗电流
- 从手机中导出 /system/framework/framework-res.apk 文件
- 使用反编译工具对导出文件framework-res.apk 进行反编译
- 查看power_profile.xml 文件在frame-res 反编译目录路径: /res/xml/power_profile.xml
https://source.android.com/devices/tech/power
https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/xml/power_profile.xml
如何监测系统的电量消耗:可以通过dumpsys batterystatus 导出
adb shell dumpsys batterystats > 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
| 编号 | 测试方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 1 | 稳压电源+ 电流仪 | 整机电流 | 以测试整机电流,而且数据精确 | 需要准确硬件工具,测试操作复杂,而且不能准确测试APP消耗电量 |
| 2 | dumpsys batterystats | App电量 | 有耗电量的详细数据 | 结果可读行比较差 |
| 3 | 系统“耗电排行” | APP电量 | 直观,跟用户看到的一致 | 没有详细的数据 |
| 4 | Battery History | App电量 | 结果直观,有耗电量的详细数据 | 适用与Android 5.0及以上系统 |
bug report
Battery Historian
//7.0和7.0以后
$ adb bugreport bugreport.zip
//6.0和6.0之前:
$ adb bugreport > bugreport.txt
//通过historian图形化展示结果
python historian.py -a bugreport.txt > battery.html
Android 耗电优化历史
野蛮生长: Pre Android 5.0
特点: 电量问题排查复杂。无论是电量的测量,还是耗电问题的排查都异常艰难。
- 耗电与安装应用程序的数量有关,用户安装越多的应用程序,无论是否打开它们,手机耗电都会很快。
- App耗电量与APP使用时间无关,用户希望 App 的耗电量应该与它的使用时间相关,但是有些应用即使常年不打开,依然非常耗电
逐步收紧:Android 5.0 ~ Android 8.0
https://developer.android.com/about/versions/android-5.0?hl=zh-cn
Android 6.0 开始~, Google开始着手清理后台应用和广播来进一步省电
特点:
- 省电模式不够省电
- 用户对应用控制力度不够
3。 Target API 开发者响应不积极
最严格:Android 9.0
耗电优化与线上监控
耗电优化
- 后台耗电(厂商预装项目要求最严格的正是应用后台待机耗电)
- 用户认为应用的耗电量应该是和使用时间有关的
- Camera, Audio, Video,Bluetooth, Network, Sensor. Radio, Screen,WiFi, CPU, GPS
- 符合系统规范,让系统认为你耗电是正常的
- Android Vitals
耗电优化难点
- 缺乏现场,无法复现
- 信息不全,难以定位
- 无法评估结果
耗电优化的方法
- 常见的耗电方式
- 某个需求场景
- 代码的Bug
- 优化方法
- 利用厂商通道或定时拉去最新消息
耗电监控
- Android Vistals
- 耗电监控应该监控什么
- 监控信息---> 系统关心什么,我们就监控什么(后台耗电监控 ) Alarm wakeup、WakeLock、WiFi scans、Network
- 现场信息---> 完整的对栈信息
- 提炼规则---->将监控的内容抽象成规则
常用的规范
| APP活动 | 前台规则 | 后台规则 |
|---|---|---|
| Alarm | 单个Alarm每小时不能启动超过20次 | 单个Alarm 每小时不能启动超过10次 |
| WakeLock | 单个WakeLock每小时不能超过30分钟或者超过20次 | 1. WakeLock每 小时不能超过30分钟; 2.单个WakeLock每小时不能超过10分钟或12次 |
| Sensor | 不监控前台sensor使用 | 每小时使用不能超过30分钟 |
| WIFI scans | 每小时扫描不允许超过10次 | 每小时扫描不允许超过4次 |
| Bluetooth scans | 每小时扫描不能超过10次 | 每小时扫描不允许超过4次 |
| GPS | 每小时使用不能超过30分钟 | 每小时使用不能超过15分钟 |
| Camera | 不监控前台相机使用 | 后台不允许使用相机 |
| Network | 不监控前台网络耗电 | 每小时后台网络数据量不能超过10MB |
| CPU | 不监控前台CPU | 后台CPU持续超过30分钟 |
如何监控耗电
Java Hook
WakeLock
1. https://androidxref.com/7.0.0_r1/xref/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
2. https://developer.android.com/training/scheduling/wakelock
// 代理PowerManagerService
ProxyHook().proxyHook(context.getSystemService(Context.POWER_SERVICE), "mService", this);
@Override
public void beforeInvoke(Method method, Object[] args) {
// 申请Wakelock
if (method.getName().equals("acquireWakeLock")) {
if (isAppBackground()) {
// 应用后台逻辑,获取应用堆栈等等
} else {
// 应用前台逻辑,获取应用堆栈等等
}
// 释放Wakelock
} else if (method.getName().equals("releaseWakeLock")) {
// 释放的逻辑
}
}
**Alarm **: 用来执行定时的重复任务
- https://developer.android.com/training/scheduling/alarms
- https://developer.android.com/reference/android/app/AlarmManager.html#ELAPSED_REALTIME_WAKEUP
- https://developer.android.com/reference/android/app/AlarmManager.html#RTC_WAKEUP
// 代理AlarmManagerService
new ProxyHook().proxyHook(context.getSystemService
(Context.ALARM_SERVICE), "mService", this);
public void beforeInvoke(Method method, Object[] args) {
// 设置Alarm
if (method.getName().equals("set")) {
// 不同版本参数类型的适配,获取应用堆栈等等
// 清除Alarm
} else if (method.getName().equals("remove")) {
// 清除的逻辑
}
}
后台CPU
对于 GPS 监控,我们可以通过 Hook 代理LOCATION_SERVICE。对于 Sensor,我们通过 Hook SENSOR_SERVICE中的“mSensorListeners”
通过 Hook,我们可以在申请资源的时候将堆栈信息保存起来。当我们触发某个规则上报问题的时候,可以将收集到的堆栈信息、电池是否充电、CPU 信息、应用前后台时间等辅助信息也一起带上