Android下APP耗电量统计实现

设置中的电量统计即系统调用内部API的统计结果。

http://www.autooo.net/classid164-id138050.html:智能手机的耗电特征及APP耗电量测试的两种方法

这篇文章主要分析了耗电量的

手机发展趋势和耗电特性

智能手机越来越普及,硬件处理能力越来越强,支持的软件应用越来越多。

我们不免也有另一种担心,现在的智能手机的待机时间已经是一个短板,未来的智能手机待机时间变得更加短呢?

从理论上来说,更大的显示屏、更多核的处理器、更多样化的无线连接技术等肯定会增加手机的耗电量。同时,手机的功能也越来越多,手机使用的频率也会越来越频繁,因此对手机的耗电性能进行优化和管理也变得更加重要。譬如在不操作手机时更快的进入休眠,采用脉冲形式发射无线信号,非连续接收及非连续发射等。

为了优化和改善手机的耗电,就必须先了解手机耗电的特征或发现手机存在哪些耗电性能上的缺陷,做到有的放矢。

手机耗电的特征可以归纳为以下几个方面:

1.电流幅度波动大:

a) 关机漏电流:~10 to 100nA

b) 休眠电流: ~100 μA to 10mA

c) 待机电流: ~5 to 50 mA

d) 接收状态: ~100 to 300 mA (脉冲)

e) 发射状态: ~1 to 2.5 A(脉冲)

f) 应用软件运行:~100mA to 2A

2.电流变化的速度快、动态大:

3.电流是多种功能综合,难以区分是谁引起的电流:

4.通常需要长时间的对电流进行跟踪和观察


应用软件需要消耗电量,大家都容易理解,但究竟哪个软件耗电大,哪个耗电量小,如何定量的评估呢?估计这个问题一直困扰着很多软件开发者。相比硬件的耗电非常的直观,如显示屏的亮度等级耗电是非常容易对比和测量。软件的耗电比较抽象和难以测试,是因为软件必须运行在手机硬件的基础上,测量到的耗电量是软件与手机的硬件综合叠加的耗电总和。对于耗电来所我们无法认为的剔除硬件的耗电而单独测试软件的耗电。

所以,如何测试软件的耗电?我建议软件开发者可以有以下两种方法:

一、横向对比法:分两步测试,第一步测量手机硬件运行消耗的电量;第二步测试手机硬件和软件运行综合的耗电量。最后将两次的耗电进行对比,得出软件消耗的电量大小;

二、纵向对比法:同时进行两台手机,其中一台手机不运行软件,另一台手机运行软件;分别获得两台手机的耗电量,对比两台手机耗电量差异,得出软件消耗的电量大小。

这两种方法在测试上有一定的差异,很多应用软件都是需要移动网络的支持,而手机接收的网络信号时会随时间变化的。

因此、第一种方法的误差主要来源于手机在不同的基站网络下工作状态的变化。

第二种方法的差异则体现在两台手机本身硬件消耗的电量可能存在差异,所以需要尽可能使用同一型号,同一批次的手机。而且需要使用多通道的耗电测试仪表,如之前提到的安捷伦N6705B可以最多同时支持4台手机并行测试。



基础概念

    1. 手机由众多“部件”组成,所谓“部件”是指:CPU,WIFI,GPS....所以,Android App消耗总电量为 App运行过程中,涉及各部件的消耗电量的总和。
    2. 假设运行App导致CPU运行,时间:t,
                                               CPU单位时间消耗电量:w,
则App的CPU耗电量为:W = w*t,而有物理公式 W = U*I*t(U:电压值,I:电流值),在手机中,一般U恒定不变,所以,可以单独通过 Q(电容量,单位: mAh)= I * t 表示电量
系统源码分析

    核心源码/packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageSummary.java

    核心类

        - BatteryStatsImpl:提供App各部件运行时间。

        - PowerProfile:提供部件电流数值。

    问题

        - Android怎样存储与读取App耗电量信息(即:BatteryStatsImpl数据怎么来的?)

        - Android怎么

部件电流数值(即:PowerProfile数据怎么来的?)

        - Android具体耗电量计算方法


隐藏的方法如果直接调用会提示相关错误;







http://www.eoeandroid.com/forum.php?mod=viewthread&tid=254484&fromuid=604899
本文介绍不通过反射和编译源码使用隐藏API和Internal包。

首先我们要明白为什么隐藏API(有@hide标记)和Internal包不能使用。
当我们使用android的SDK进行开发的时候都会用到一个非常重要的jar文件--android.jar(SDK_DIR/platforms/platform-X/android.jar,X是API等级)。这个包中移除了所有被标记的尾@hide的类、方法、枚举、字段和Internal包。当我们的程序在设备上运行的时候会加载设备上的一个framework.jar的文件,它包含了移除的部分。

所以我们的思路是想办法得到framework.jar将那些缺少的API拿出来放到android.jar中。当然这些都可以通过编译源码来解决,但那样太过麻烦。下面我介绍一种简单的方法来获取这些内容。

1,获取framework.jar
刚才也说了,设备上有这个文件,那么我们就从设备上来取。通过adb pull命令(也可以用DDMS):
adb pull /system/framework/framework.jar
我们需要该包中的classes.dex文件。这里从4.0以下模拟器上取,真机上一般是经过优化了的.odex文件,而4.0以上的模拟器中的这个文件中没有classes.dex文件。
如果需要4.0以上的可以在网上找找。

2,将framework.jar改成framework.zip,解压,得到里面的classes.dex文件。

3,将.dex文件转换成.jar格式。这里用到了dex2jar这个工具,知道反编译的同学都知道这个工具,没有的可以在网上找找。
转换结束后会得到一个classes.dex.dex2jar.jar文件。

4,重命名classes.dex.dex2jar.jar成classes.dex.dex2jar.zip,解压。得到以下文件。



http://bbs.51testing.com/thread-1045004-1-1.html
针对Android的电量优化,应该是很多开发者最关心的几大问题之一。
最近我正在看相关的资料,希望跟大家一起探讨学习。

关于电量,我们最需要处理的问题就是两个方向:
(1)第三方APP和系统本身是如何获取电量值并展示的?我们能否针对他们的算法做适度优化?或者规避部分统计?
(2)其他代码级别优化。从业务逻辑出手,架构上适度调整的手段有哪些?

就上面2个问题,我们开始展开讨论:
(1)APP获取电量算法。
经过查看源码,以及各种资料。最后我们看到app计算电量的算法如下:

在主Activity里面 info.getBatteryStats() 就搞定了。 首先 load(),如果load失败,走CPU时间计算,通过getAppListCpuTime这样函数。 1
CPU的时间计算,有3个核心步骤:
1 ActivityManager遍历runningApp进程,获取对应pid
2 getAppProcessTime(pid)通过读取/proc/pid/stat文件,拿取APP在CPU的运行时间。
3 重新为BatterySipper附值:+time;
2

获取APP消耗 processAppUsage();也分三步走:
1 通过PowerProfile 获取cpu的速度层次(speedsteps),方便后面使用
2 根据不同CPU的速度等级,计算cpu在某个速度下的电量,mA毫安
3

mPowerProfile.getAveragePower(PowerProfile.POWERCPUACTIVE,p)
很多地方都用刀这个API获取power。
那它究竟做了些什么呢?
查看系统源码可以知道:
实际上这句话是获取1个叫PowerMap的数据结果,获得电量。而PoweMap的赋值,是来源于com.android.internal.R.xml.powerprofile 的文件。
关于该文件的获取 android-版本号/core/res/res/xml/powerprofile.xml

计算各种耗电量的详细算法是:
(1)计算CPU 
BatteryStateImpl.getUidStats()获取数据源
遍历数据源,得到ProcessState
通过ProcessState拿到以下几个指标
userTime
systemTime
foregroundTime
CpuFgTime(cpuFgTime+= foregroundTime10;)
cpuTime (cpuTime+=tmpCpuTime)
tmpCpuTime=(userTime + systemTime)10

      power +=processPower
4

(2)计算wake lock 
wakelock,只关心partial的类型
同样地,通过uid获取wakelock的信息 u.getWakelockStats()

     遍历这个map,获取wakeTime次数
     wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);

     计算总耗时 wakelockTime += timer.getTotalTimeLocked(uSecTime,which);
     计算总电量 poewer+= 
6
(3)数据通信的消耗
7
(4)wifi运行消耗
8
(5)传感器等等

processMiscUsage()
9
以wifi为例,是所以wifi的电量(wifi使用电量+appwifi使用电量)
addwifiusage()为例子:
long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000; //获取wifi总时间
long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000; //获取wifi运行时间
runningTimeMs -= mAppWifiRunning;//这里计算的是非app使用wifi的量(时间)

double wifiPower = (onTimeMs * 0 * mPowerProfile.getAveragePower(PowerProfile.POWERWIFION) + runningTimeMs
* mPowerProfile.getAveragePower(PowerProfile.POWERWIFION)) / 1000;//时间*电量==毫安时


http://jingyan.baidu.com/article/ca41422fc76c4a1eae99ed9f.html


将外部包导入工程的方法:
输入文件夹名称【lib】,点击【ok】。我们通常在lib文件夹中存放从外部引入的jar包
找到我们要引入的jar包,鼠标选中jar包,然后按住鼠标左键不放,把jar包拖到lib文件夹中。或先复制jar包,然后在lib文件夹上右击,选择复制。此时,打开选择框,我们选择默认的【copy files】,点击【OK】关闭。然后我们就可以在lib文件夹下看到我们复制成功的jar包。

此时,只是把jar包复制到项目中,还不能使用。我们再在项目名上右击,依次选择

【Build Path】-->【Configure Build Path...】。

在打开的窗口中,先选中【Libraries】页,再从右边的按钮中点击

【add JARs...】

在打开的窗口中,我们依次展开本项目的项目和lib文件夹,然后选中我们刚才复制到项目中的jar包,然后点击【OK】关闭窗口
此时,我们在刚才打开的【Libraries】页中可以看到我们引入的jar包的名称。点击【OK】确认。

http://mengren425.blog.163.com/blog/static/569039312015016104050168/
Android4.4耗电量的获取:
首先解释下各软硬件耗电量的计算。假设设备(如WIFI)单位时间内消耗的电量为w,运行时间为t,则其在这段 时间内的耗电量为W=w*t。根据物理学中的知识,电功率(即所谓电量)计算公式为W=UIt,其中U为电压值,I为电流值,t为运行时间。由于在一部机 器中,电压值U是恒定不变的(一般如此),因此可以忽略掉参数U,单独通过电流及时间即可表示电量(比如电池容量为2000mA、2500mA等,以mA 为单位进行恒量)。根据以上描述,只要我们获得了某程序或某设备运行的时间,以及其运行时所需要电流值,则可以计算出其消耗的电量(以上理论会在代码中体 现)。

           某程序或硬件设备的运行时间可以分别通过BatteryStats.Uid.Proc和BatteryStatsImpl中的相关接口获得(后文分析代码时会见到),本文主要讲下电流值(即单位时间消耗电量)的获取。

1. BatteryStatsHelper.java
adnroid系统中提供的接口
/frameworks/base/core/java/com/android/internal/os/
BatteryStatsHelper.java中BatteryStatsHelper此类提供了如下接口:
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,long rawUptimeUs)
会刷新系统电量使用情况,会统计app 或者是某个外设具体使用电量情况。
例addWiFiUsage() 
 long idleTimeMs = (mTypeBatteryRealtime- mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;//运行时间
 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))//消耗电量

2. PowerProfile.java
adnroid系统中提供的接口
/frameworks/base/core/java/com/android/internal/os/PowerProfile.java中PowerProfile此类提供了如下接口:

  (1)public double getAveragePower(String type)
           此方法返回在type子系统下消耗的电流值,单位为mA。type可取PowerProfile中定义的常量值,包括 POWER_CPU_IDLE(CPU空闲时),POWER_CPU_ACTIVE(CPU处于活动时),POWER_WIFI_ON(WIFI开启时) 等各种状态。例如,如下调用getAveragePower(POWER_CPU_ACTIVE)将返回CPU处于活动时的电流 值;getAveragePower(POWER_WIFI_ON)将返回维持WIFI启动状态所需的电流值。结合之前的描述,假设WIFI开启的时间为 t(假设此段时间未使用WIFI传输数据,因为WIFI传输数据需要额外的电能消耗),那么在此段时间内WIFI所消耗的电量为 W=t*getAveragePower(POWER_WIFI_ON)。

   (2) public double getAveragePower(String type, int level)
           相比于方法(1),此接口需要传入参数level,现在来解释下level的含义。我们知道,android系统中CPU可以以多种速度运行(假设 分别为600MHz,800MHz,1GHZ等),速度不同时CPU消耗的电量也不同,参数level即代表不同的运行频率,显然,方法 getAveragePower(String type, int level)将返回type子系统在CPU运行速度级别为level时单位时间内所消耗的电量(即电流值)。
   (3)public double getBatteryCapacity()       获取电池总电量。
  (4)public int getNumSpeedSteps()        获取CPU可以以几种速度运行。

3. power_profile.xml
           事实上,通过阅读PowerProfile.java代码及相关注释即可知,此类中各接口返回的电流等数值都是通过读取 power_profile.xml文件获得的,即各种子系统消耗的电量值、CPU运行速度值、总电量等信息都是以固定值的形式存储在 power_profile.xml中。由于硬件之间的差异,各子系统耗电信息是不同的,因此此文件需要各生产厂商进行定制。android系统原生的 power_profile.xml文件的存放路径为:frameworks/base/core/java/com/android/internal /os/PowerProfile.java,经过各硬件厂商定制后,存放路径可能发生变化,如三星某型号的power_profile.xml路 径:device/samsung/maguro/overlay/frameworks/base/core/res/res/xml /power_profile.xml,其内容如下
<device name="Android">
  <!-- Most values are the incremental current used by a feature,
       in mA (measured at nominal voltage).
       The default values are deliberately incorrect dummy values.
       OEM's must measure and provide actual values before
       shipping a device.
       Example real-world values are given in comments, but they
       are totally dependent on the platform and can vary
       significantly, so should be measured on the shipping platform
       with a power meter. -->
  <item name="none">0</item>
  <item name="screen.on">370</item>  <!-- ~200mA -->
  <item name="screen.full">380</item>  <!-- ~300mA -->
  <item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->
  <item name="bluetooth.on">0.1</item>  <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
  <item name="wifi.on">30</item>  <!-- ~3mA -->
  <item name="wifi.active">215</item>  <!-- WIFI data transfer, ~200mA -->
  <item name="wifi.scan">165</item>  <!-- WIFI network scanning, ~100mA -->
  <item name="dsp.audio">0.1</item> <!-- ~10mA -->
  <item name="dsp.video">0.1</item> <!-- ~50mA -->
  <item name="radio.active">0.1</item> <!-- ~200mA -->
  <item name="radio.scanning">0.1</item> <!-- cellular radio scanning for signal, ~10mA -->
  <item name="gps.on">0.1</item> <!-- ~50mA -->
  <!-- Current consumed by the radio at different signal strengths, when paging -->
  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
      <value>0.2</value> <!-- ~2mA -->
      <value>0.1</value> <!-- ~1mA -->
  </array>
  <!-- Different CPU speeds as reported in
       /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->
  <array name="cpu.speeds">
      <value>1320000</value> <!-- 1320 MHz CPU speed -->
      <value>1272000</value>
      <value>1224000</value>
      <value>1176000</value>
      <value>1128000</value>
      <value>1080000</value>
      <value>1032000</value>
      <value>996000</value>
      <value>948000</value>
      <value>900000</value>
      <value>852000</value>
      <value>804000</value>
      <value>756000</value>
      <value>708000</value>
      <value>660000</value>
      <value>612000</value>
      <value>564000</value>
      <value>516000</value>
      <value>468000</value>
      <value>420000</value>
      <value>372000</value>
      <value>324000</value>
      <value>276000</value>
      <value>228000</value>
      <value>180000</value>
      <value>132000</value>
  </array>
  <!-- Current when CPU is idle -->
  <item name="cpu.idle">370</item>
  <!-- Current at each CPU speed, as per 'cpu.speeds' -->
  <array name="cpu.active">
      <value>630</value>  <!-- ~100mA -->
      <value>600</value>
      <value>570</value>
      <value>545</value>
      <value>520</value>
      <value>500</value>
      <value>490</value>
      <value>480</value>
      <value>470</value>
      <value>450</value>
      <value>440</value>
      <value>430</value>
      <value>420</value>
      <value>415</value>
      <value>410</value>
      <value>405</value>
      <value>400</value>
      <value>397</value>
      <value>395</value>
      <value>390</value>
      <value>385</value>
      <value>380</value>
      <value>375</value>
      <value>370</value>
      <value>365</value>
      <value>360</value>
  </array>
  <!-- This is the battery capacity in mAh (measured at nominal voltage) -->
  <item name="battery.capacity">2800</item>

  <array name="wifi.batchedscan"> <!-- mA -->
      <value>.0002</value> <!-- 1-8/hr -->
      <value>.002</value>  <!-- 9-64/hr -->
      <value>.02</value>   <!-- 65-512/hr -->
      <value>.2</value>    <!-- 513-4,096/hr -->
      <value>2</value>    <!-- 4097-/hr -->
  </array>
</device>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值