本文主要通过跟踪 Android 设置 APP 的源码分析如何统计手机各个软件的电量。

·PowerProfile 简介

PowerProfile 这个类对手机的各个子系统运行时的平均电流(mA)和基本状态做了初步统计,所有信息都存放在 sPowerMap 这个 Map 里,这个 Map 实际是从 power_profile.xml 文件中读取的,PowerProfile 提供接口如 getAveragePower(String type) 之类,供我们去取该XML里的各项的value,是个很短的类,也很容易理解。

PowerProfile 是 android 的 internal 类,默认是 hidden 的,即我们第三方应用只能通过反射调、或者自己做一个完整的 android.jar 的 jar 包,或者源码下编译。对于如何做自制的android 完整 sdk 包(包含internal类)请参考https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/这一系列文章。本文分析的源码的环境均为 android-14 即 android4.0。

·PowerProfile读取基本电量状态的具体流程

  1. PowerProfile 类构造函数
如上图所示为 PowerProfiles 类的构造函数,且只有这一种构造方式,从构造函数就可以看出这个类的作用,即从XML中读取电量值,并把结果放入 sPowerMap 中。(左边的行号为实际源码的行号,以后的截图也是)。
  1. 转到这个 readPowerValuesFromXml 函数
如上图所示为 readPowerValuesFromXml 函数的开头,可以看出是从一个系统文件 *power_profile.xml* 中读取数据,这个函数就是通过引入一个叫 XmlUtils 的 internal 类来完成的(其实就是封装了 beginDocument 和 nextElement 这样控制读取游标的函数)。
  1. 那么 power_profile.xml 是个什么样的文件呢?从 AndroidXRef的4.0.3源码中查找这个文件,发现有五个:
从路径可以大体看出,不同的手机厂商可以会有不同的 power_profile.xml(这很好理解,手机耗电直接与硬件挂钩,硬件具体的耗电如何也就是只有厂商自己能够弄清楚了),而这个 [/frameworks/base/core/res/res/xml/power_profile.xml][5](点击可以查看),就是一个大体的格式了。

这里面的一些标签对应到 PowerProfile 里常量对应关系和具体解释如下:

POWER_NONE = "none"
即没有功耗(power consumption),一般是0POWER_SCREEN_ON = "screen.on";
屏幕亮的时候的功耗(不考虑背光的电量)POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
蓝牙驱动在发送/接收数据时候的功耗POWER_BLUETOOTH_ON = "bluetooth.on"
蓝牙为开启状态时的能耗POWER_SCREEN_FULL = "screen.full"
背光为最亮的时候的能耗,不是最亮的时候耗电为线性关系,即 50% 亮度时这个值应该乘以 0.5POWER_WIFI_ON = "wifi.on"
WiFi 为开启状态时的能耗POWER_WIFI_ACTIVE = "wifi.active"
WiFi 驱动在发送/接收数据时的能耗POWER_WIFI_SCAN = "wifi.scan"
WiFi 在扫描可用网络时的功耗POWER_AUDIO = "dsp.audio"
播放后台音频内容时音频硬件产生的功耗,除了 CPU 外,也可能由 DSP 或 amplifier 引起(数字信号处理相关的硬件)POWER_VIDEO = "dsp.video"
播放后台多媒体内容时的多媒体硬件产生的功耗,除了 CPU 外,也可能由 DSP 引起POWER_RADIO_ACTIVE = "radio.active"
接通电话状态下的功耗POWER_RADIO_SCANNING = "radio.scanning"
移动网络(cell radio 可以翻译为蜂窝移动网络) 在搜寻信号的时候的功耗POWER_GPS_ON = "gps.on"
GPS 为打开状态时的功耗POWER_RADIO_ON = "radio.on"
移动网络为开启时但不是在打电话时的功耗,数组,对应不同的信号强度POWER_CPU_SPEEDS = "cpu.speeds"
CPU 的几种频率,数组,支持变频的手机会有多个,400000 即 400MHzPOWER_CPU_IDLE = "cpu.idle"
PowerProfile.java 里的注释为“电池处于休眠模式(power collapse mode)时的 CPU 功耗”,power_profile.xml 里的注释为“cpu 挂起时的功耗”,我认为可以理解成 idle 进程 cpu 功耗。POWER_CPU_AWAKE = "cpu.awake"
CPU wake lock 被持有时的功耗,This should be 0 on devices that can go into full CPU power collapse even when a wake lock is held. POWER_CPU_ACTIVE = "cpu.active"
CPU 在不同频率下的功耗
  1. 解析 power_profile.xml 这个文件的数据放在了 sPowerMap 中,并对外提供了四个接口:getAveragePower(String type) 返回指定 type 的功耗,即 power_profile.xml 中对应标签的值,单位为毫安(milliAmps);getAveragePower(String type, int level) 返回指定 type 的数组类型的下标为 level 的值,单位为毫安;getBatteryCapacity() 返回电池容量;getNumSpeedSteps() 返回 "cpu.speeds" 对应的值的数量,即 CPU 可以变频的个数。

·电量统计源码

  1. 电量统计的源码路径在 /packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageSummary.java
上图代码段为 onCreate() 获取了 batteryinfo 服务的接口:IBatteryStats![clipboard4.png][8]上图代码段为 onResume() 中 执行refreshStats(),并注册了电池变化的reciever(有变化时刷新)![clipboard5.png][9]上图代码段为 refreshStats() 函数执行了 load 方法![clipboard6.png][10]上图代码段为在 load() 中拿到了电池相关的统计数据(其实就是解析的 batterystats.bin 文件,该文件的说明参考[http://jingyan.baidu.com/article/39810a23c090bcb636fda6a6.html][11]),数据为 BatteryStatsImpl 类型,该类比较冗长复杂,我们通过系统电量 apk 的源码来看如何去利用该类的信息。
  1. refreshStats() 基本流程分两步:processAppUsage() 与 processMiscUsage(),分别是统计软件电量与硬件电量,我们这里只看软件电量,也是我们最关心的。

    BatteryStatsImpl 提供的接口基本都是次数和时间,配合 PowerUsage 来计算电量,控制统计时机的有个叫 whitch 的参数贯穿统计过程,它有四种取值:BatteryStats.STATS_SINCE_CHARGED=0 表示统计所有数据,包括以前的数据;BatteryStats.STATS_LAST=1 表示只统计上次的数据、BatteryStats.STATS_CURRENT=2 表示只统计当前这次的数据、BatteryStats.STATS_SINCE_UNPLUGGED=3 表示只统计自从上次拔掉设备之后的数据。
    
    1. 所有的软件电量数据统计单位为 uid,通过 getUidStats 获取所有软件电量数据:

    (在 android 里,一个 Uid下 可能有来自多个应用的进程甚至是 /system/bin 下的程序,如具有相同签名并设置了 shareduid 的三方应用,特别是一些系统应用,这就导致了电量统计粒度无法非常准确的定位到某一个应用 - 包名)

    1. 接下来遍历该 Map 计算每个 Uid 所耗的电量

       ![clipboard8.png][13]调用 Uid 的 getProcessStats 方法,能够得到该 Uid 下每个进程的 CPU 使用情况,接下来继续遍历这个 processStats:

        通过439行的那句Log可以看出该 Map 的 key 是进程名,其实这样我们可以通过进程名反查其所属包名来统计各个应用的具体情况(但某些情况下,如 shareduid 相同并指定组件的进程名相同时,多个应用也可以共享一个进程,可以简单参考[http://mypyg.iteye.com/blog/720406][15]的结论),其中 userTime 和 systemTime 分别为该进程的用户态时间和内核态时间,foregroundTime 是前台运行时间,通过注释可以看出他们三个的单位均为 1/100 秒(但是 getForegroundTime 的方法注释标明返回为微秒,这很奇怪,不过鉴于系统的电量APK都这么做了,那就可以简单的认为方法注释写错了),接下来的 446 - 458 行其实就是按各个 CPU 频率的时间比来统计平均电量(这里的电量计算公式为 功耗(电流mA) * 时间,即 I * t,无法理解,因为从初中就学到电量损耗单位是J,公式为 W = U * I * t = I * I * R * t,其中 U 为 电压,R 为电阻),packageWithHighestDrain 是记录了该 Uid 下的耗电最多的呢个进程名。该 Uid 下的所有进程的 CPU 时间和前台时间都累加起来,分别为 cpuTime 和 cpuFgTime,单位为毫秒。跳出这个遍历 Map 就又出现了一段异常代码:![clipboard10.png][16]如果前台时间大于 CPU 时间,那么强制的把 CPU 时间置为前台时间,至于为什么这么做,以及能不能把前台时间强制置为 CPU 时间,都是未知数,均是电量统计误差导致的。不同的三方应用应该也有自己的矫正计算方法。3. 然后是计算 CPU wake lock 持有的电量消耗,通过 Uid.getWakelockStats() 得到:

        在这里他只是考虑了 WAKE_TYPE_PARTIAL 的情况,注释说明因为 WAKE__TYPE_FULL 的 wake lock 在关屏后就会呗取消,所以只需要关心 partial wake lock,不明觉厉 ... 相同的,通过 getAceragePower 得到对应功耗,乘以时间即耗电量。注意下,这个 Map 里的 Key 这是个耗电者的说明,既不是包名也不是进程名(再次导致统计粒度加粗)。4. 然后是计算 TCP 数据传输的功耗,首先 getAverageDataCost 函数计算了数据传输的平均耗电,即每传送 1B 数据所耗电量:![clipboard12.png][18]可以看出它包含了 wifi、移动网络的因素,粗略得算了下平均耗电,然后通过 Uid.getTcpBytesReceived 和 Uid.getTcpBytesSent 来获取 TCP 数据传输大小,相乘即得到耗电量:

5. 接下来是保持 WiFI 运行的电量损耗,通过 Uid.getWifiRunningTime() 得到 WiFi 占有时间:

    与前面计算 CPU 耗电、CPU wake lock 耗电一样,WiFi开启状态时的功耗电流 * 开启的时间。6. 系统电量应用统计的软件电量最后一项,即软件的传感器耗电,通过 Uid.getSensorStats() 得到:

    可以看出,GPS 耗电时间能够单独统计出,其他的不能,其实 power_profile.xml 里的感应器也只有 GPS 的功耗,Uid.Sensor 中也只有 GPS 这一个常量。7. 至此为止,系统电量应用的软件统计代码已经完了,剩下的是归纳和计算百分比。每个 Uid / 硬件电量基本统计结果存为一个个的BatterySipper,接下来通过判断 Uid 是否为第三方应用来将刚才的统计归类:

Android 系统电量统计相关推荐

  1. android系统电量优化,基于Android系统网络耗电量优化方法的.pdf

    基于Android系统网络耗电量优化方法的 2012年第10期,第 45卷 通 信 技 术 Vol.45,No.10,2012 总第250期 Communications Technology No. ...

  2. Android 系统(42)---使用BatteryHistorian分析和优化应用电量

    使用BatteryHistorian分析和优化应用电量 在Android项目中, 较难监控应用的电量消耗, 但是用户却非常关心手机的待机时间. 过度耗电的应用, 会遭到用户无情的卸载, 不要存在侥幸心 ...

  3. Android电量统计

    Android电量统计 前言 在维护电量管家应用以及学习处理一些功耗问题的时候,经常会接触电量统计相关的知识,抽空总结下这块知识,方便自己以及他人的学习. 电量统计 概述 在Andorid系统中的电量 ...

  4. Android系统默认显示电池电量百分比

    Android系统默认显示电池电量百分比 整理一下修该需求时候遇到的问题以及进行修改的思路: 需求:Android 11 让系统默认显示电池电量百分比 整理一下修该需求时候遇到的问题以及进行修改的思路 ...

  5. 安卓修改电池容量教程_安卓(Android)系统电池电量修改图文教程

    安卓( Android )系统电池电量修改图文教程 有机友不喜欢新 rom 的电池图标, 想要官方的原版电池 图标,也有机友想更换其他电池图标.为了方便想更换电池 图标的机友,发一个最简易的教程. 在 ...

  6. s7android系统电量,三星S7 Edge升级Android 7.0续航测试:超后悔

    每当谷歌发布新一代Android系统时,不少手机用户就希望厂商能够尽快推送更新进行升级.但有时候升级至最新版系统可能并不是一件好事. 近日,三星为旗下Galaxy S7系列手机推出了Android 7 ...

  7. 趋势畅想-搭载android系统的智能数码相机

    引言 虽然当下的智能手机都具备拍照功能,但相比主流卡片数码相机而言,手机的拍照功能都还显得很小儿科,如高速连拍.手动白平衡.1厘米微距.广角等功能都鲜有实现,成像效果与同成像分辨率的卡片机相比也差很多 ...

  8. 深入浅出Android App耗电量统计

    原文出处:http://www.cnblogs.com/hyddd/p/4402621.html 在Android统计App耗电量比较麻烦,直至Android 4.4,它仍没公开"电量统计& ...

  9. 从源码角度看Android系统SystemServer进程启动过程

    SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...

最新文章

  1. 在虚拟机安装OpenSuse Tumbleweed
  2. 剑指Offer面试题:2.二维数组中的查找
  3. 判断一个字符串是否是数值
  4. hadoop 单机伪分布式安装步骤
  5. python调用命令行获取pid_命令行命令/命令运行时的pid及获取
  6. [转载]项目风险管理七种武器-离别钩
  7. 【Deep Learning 四】课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)答案
  8. access 报表中序号自动_数据报表多种序号生成方式
  9. 人工智能 一种现代方法 第2章 智能Agent
  10. 网管软件如何部署?网管常用软件
  11. vue校验输入框不能有中文
  12. 狄利克雷卷积_狄利克雷卷积学习笔记
  13. 安工大Linux程序设计实验
  14. android系统GUI设计师必会资源图制作工具
  15. 1.5.33 计算分数加减表达式的值
  16. 关闭msmpeng_关闭Windows Defender与性能提升测试
  17. elastic不错的官方文档(中文)
  18. 第十五期】Monica:单身滴美女程序员 多图!
  19. wifi连接速率 linux,Deepin linux 操作系统提高 WiFi 速度
  20. wannafly summer camp day6

热门文章

  1. TeamViewer远程控制华为设备方法
  2. stm32cube,hal库来实现PS2手柄数据发送
  3. 图片鼠标移入图片改变颜色、显示另外一张图片(2种方式)
  4. 高斯-约当消元法(转)
  5. 谷歌浏览器播放h.265_Google删除H.264
  6. ug12在win8计算机名错,Win8安装UG9.0时出错提示“UGII_TMP_DIR 被设为一个有无效(非ASCII)字符的目录”怎么办...
  7. 云片:批量发送超级短信示例
  8. 视频 | 苏炳添的“冠军卧室”曝光,来看看百米飞人的另一面
  9. 【干货】剖析大数据分析方法论的几种理论模型(文末有福利哦)
  10. 大学物理 狭义相对论 思维导图总结