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找到电源配置文件
如何在不同厂商中获取耗电流

  1. 从手机中导出 /system/framework/framework-res.apk 文件
  2. 使用反编译工具对导出文件framework-res.apk 进行反编译
  3. 查看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

特点: 电量问题排查复杂。无论是电量的测量,还是耗电问题的排查都异常艰难。

  1. 耗电与安装应用程序的数量有关,用户安装越多的应用程序,无论是否打开它们,手机耗电都会很快。
  2. 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开始着手清理后台应用和广播来进一步省电
特点:

  1. 省电模式不够省电
  2. 用户对应用控制力度不够
    3。 Target API 开发者响应不积极

最严格:Android 9.0

耗电优化与线上监控

耗电优化

  • 后台耗电(厂商预装项目要求最严格的正是应用后台待机耗电)
    • 用户认为应用的耗电量应该是和使用时间有关的
    • Camera, Audio, Video,Bluetooth, Network, Sensor. Radio, Screen,WiFi, CPU, GPS
  • 符合系统规范,让系统认为你耗电是正常的
    • Android Vitals

耗电优化难点

  1. 缺乏现场,无法复现
  2. 信息不全,难以定位
  3. 无法评估结果

耗电优化的方法

  • 常见的耗电方式
    • 某个需求场景
    • 代码的Bug
  • 优化方法
    • 利用厂商通道或定时拉去最新消息

耗电监控

  1. Android Vistals
  2. 耗电监控应该监控什么
    • 监控信息---> 系统关心什么,我们就监控什么(后台耗电监控 ) 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 信息、应用前后台时间等辅助信息也一起带上