Android应用的耗电量统计

一, 前言

Android应用的耗电量, 一直是大家比较关心的问题。普通用户,一般通过“设置”中的“电池”来看排名前10的应用,或者凭借手机续航时间的变化来粗略估计;专业的测试人员,一般采用电流表读取整部手机的电流,再将应用运行时的电流和手机空跑时的电流对比。

这些方法都存在较为严重的问题:

l   “设置”中的“电池”选项,只是一种排名,缺少具体的耗电量数据。如果应用耗电量较小,甚至不会展示在界面上;

l   电流表只能读取整部手机的耗电。由于Android系统和屏幕是耗电大户,这两者的风吹草动,很可能掩盖掉被测应用的耗电!

为了解决当前的问题,讯飞iTest小组对Android的耗电量进行了一次较为深入的调研,给出了一种“新”的解决方案。(然并卵)

 

二, Android源码初探

Android的一大优势, 就是开源。在排除了linux的耗电量统计方案之后, 我们将目光聚焦在Android的”设置”的源代码中:Android的耗电量排行是怎么算出来的呢?

扒开源代码瞧了一下,发现这里别有洞天!Android其实很早就开始做耗电量的统计,已经取得了长足的进步,比linux的耗电量统计强了不止一个等级。google一直不开放相关的API, 可能是因为这个功能还不成熟吧。对比了一下Android4.1, android4.4, android5.1的源代码, 发现思路是一致的, 但后面的版本比前面的版本更加细致, 且使用的类也有所变化。 为简单起见,这里以androd5.1版本为例进行讲解。相关的源码我会和本文一起打包, 有兴趣的同学可以看一下。

代码的入口,当然是“设置”中的“电池”界面啦。为了省事,大家直接看这个包“com.android.settings.fuelgauge”, 优先看里面的PowerUsageSummary类。它的注释这样写到:“Displaysa list of apps and subsystems that consume power, ordered by how much power wasconsumed since the last time it was unplugged.” 大意是说, 将应用自从上次拔下USB线后的耗电量排成列表。 我们就从这里一步步深入代码。

这段注释有问题,android4.4以前的系统默认以上次拔下USB线为起点计算耗电量,android4.4-5.1是以上次充满电为起点)

PowerUsageSummary类的作用,是筛选耗电量最多的前10个应用,并且展示。真正计算耗电量数据的,是com.android.internal.os.BatteryStatsHelper, 它计算所有应用的耗电。这个计算方法很有趣, 有点象在超市购物: 有一张“价格表”,记录每种硬件1秒钟耗多少电。有一张“购物清单”,记录apk使用了哪几种硬件,每种硬件用了多长时间。假设iTest这款apk累计使用了60秒的cpu,cpu1秒钟耗1mAh。那iTest本次就占用了60mAh的电。实际的算法比这个例子复杂很多。有兴趣的同学可以查看BatteryStatsHelper类的processAppUsage方法。

从这里可以看出,android自带的耗电量统计的准确性,受两个大方面的因素影响:


一是那张“价格表”,由PowerProfile类提供。手机的硬件是各不相同的,所以每一款手机都会有一张自己的“价格表”。这张表的准确性由手机厂商负责,所以,尽量用大厂的机子,并且只使用该厂商提供的Android系统。 

 

二是那张“购物清单”,这是Android的BatteryStatsService类提供的。上文说到的BatteryStatsHelper类使用AIDL调用BatteryStatsService类的getStatisticsStream方法获取相关数据。

 

 

三, Android耗电量统计细节

接下来,我们分析android5.1的源代码中apk耗电量统计的部分。BatteryStatsHelper的ProcessAppUsage方法, 位于BatteryStatsHelper.java文件的390行。 以uid为单位,依次统计每个apk的cpu耗电量\wake lock耗电量 \移动数据耗电量\wifi数据耗电量\wifi维持耗电量\wifi扫描耗电量\传感器耗电量(多种) 。

 

1, cpu耗电量统计

    如下图所示, cpu耗电量计算前,做了一些有意思的事情。 这是因为, cpu平时并不是满负荷运转的,为了节能, 会将cpu速度限定几个档次。很明显,在cpu低速时运行1秒,要比cpu高速时运行1秒省电。 


这样算虽然精确,但也给统计带来了不小的麻烦。 不旦要统计cpu 时间,还是统计cpu时间所处的cpu速度级别:

 

从这段代码中, 我们发现Android的耗电量统计做得还比较有诚意。 联想到有些工具,以cpu时间来直接估算apk的耗电量, 与google的方法真是差了好几条街。

 

2, wake lock 耗电量统计

       大家知道,手机为了省电,一段时间无操作,就会进入休眠状态。 如果应用获取了wakelock,就会阻止手机休眠!由此产生的耗电量,当然要算在应用头上。

       看代码478行可知, google目前只统计了Partialwake lock,而传说中的wake lock有四种,貌似这部分代码还有待改进。


    别外, wake lock会影响cpu,屏幕和键盘灯, 488行代码显示只统计了cpu的耗电量( partialwake lock 只影响cpu)。

 

 

3, 移动数据耗电量统计

       早期的android版本, 移动数据耗电量和wifi数据耗电量是混在一起计算的。 分开计算,是一大进步, 只不过Android统计流量的时候,还得分清楚哪些流量走的是移动网络,哪些走的是wifi。复杂度明显的上升了。


   从第499行可以看出, 如果移动网络运行的时间大于0,则根据运行的时间计算耗电量, 否则根据流量算耗电量。

第510行有一个小插曲:根据流量计算耗电量时,其实是根据packet个数来计算耗电量,而不是根据流量大小。496,497行统计了收发的字节数,但这里没用上。

细究起来的话, 移动网络也分2G, 3G, 4G, 不同网络下耗电量也不相同, google以后会不会分得更细? (好累, 统计个电量不容易啊。)

 

 

4, wifi流量耗电量统计

       比移动数据的耗电量简单一些,略过。


 

5, wifi维持耗电量\wifi扫描耗电量

略过,过程不是很复杂,只是不明白这么做的用意,有空再研究了。

 

6, 传感器耗电量统计

       看起来代码一大段,其实逻辑较为简单,  multiplier变量是传感器单位时间的耗电量, sensorTime是传事情器使用的时间,两者相乘即可。 只不过传感器有很多,所以要用到数组,并且GPS传感器是单独计算的,才导致代码行数较多。

 

 

综上所述, 一个应用的耗电量包括以上6大类耗电因素,貌似是不包括屏幕的, 大家注意哦。

 

 

四, 权限限制

Android4.4以前的版本, 未对耗电量统计的代码做权限限制, 只需要使用java反射等手段,就可以调用相关的内部类和隐藏接口。

自Android4.4开始,Android严格限制了权限。 普通apk即使在AndroidManifest.xml中申明使用"android.permission.BATTERY_STATS", 也获取不到相关的统计数据。 新增的权限校验的代码位于BatteryStatsService类中:


 

五, iTest的耗电量统计

iTest当前最新版本为3.5.0,部分实现了耗电量统计:

l   android5.0-5.1:  支持,需root;

l   android4.4:           暂不支持, 请等待下一个版本;

l   android4.1—4.3:  支持,且不需root;

l   android4.1以前:  不支持。

    

  

    需要注意的是,由于andoid的碎片化问题严重,相当一部分手机存在兼容性问题。 使用前,请先阅读iTest的说明书等文档。

 

 

 

 

六, 一丝希望

     从android5.0开始, dumpsysbatterystats的时候,可以直接打印出应用的耗电量,如下所示:

Estimated power use (mAh):

    Capacity:2300, Computed drain: 26.8, actual drain: 0.00000000-23.0

    Uid 0: 8.28

    Screen: 7.15

    Uid u0a7:4.32

    Wifi: 2.55

    Idle: 2.20

    Cell standby:0.808

    Uid 1000:0.542

    Uid u0a658:0.378

    Uid u0a661:0.140

    Uid u0a664:0.0897

    Uid u0a12:0.0870

    Uid u0a78:0.0661

    Uid u0a19:0.0582

    Uid 1013:0.0207

    Uid u0a86: 0.0183

    Uid u0a555:0.0144

    Uid u0a91:0.0137

    Uid u0a49:0.00782

    Uid 1001:0.00772

    Uid u0a51:0.00238

    Uid 1027:0.00174

    Uid u0a99:0.00168

    Uid u0a63:0.00154

    Uid u0a3:0.00101

    Uid u0a628:0.000930

    Uid u0a95:0.000894

    Uid u0a14:0.000816

    Uid u0a5:0.000741

    Uid u0a33:0.000739

    Uid u0a16:0.000653

    Uid u0a56:0.000653

    Uid u0a29:0.000553

    Uid u0a240:0.000259

 

(这是通过一连串的调用实现的,最核心的代码位于BatteryStats.java的2848行,有兴趣的同学可以联系上下文查看。)

这可能是一个积极的信号,google对自己的耗电量统计已经比较有信心了吧。也许下一个android版本就会开放耗电量API ?期待中。。。。



备注: 本文受网友"hyddd"的文章启发, 文章地址为: http://www.cnblogs.com/hyddd/p/4402621.html  



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页