Android性能专项测试之耗电量统计API

版权声明:本文为Doctorq原创文章,未经博主容许不得转载。 https://blog.csdn.net/qhshiniba/article/details/49155979

参考文章:Android应用的耗电量统计 
深刻浅出Android App耗电量统计 
Battery stats - CPU total vs CPU foreground 
深刻浅出 Android App 耗电量统计 
浅析Wakelock机制与Android电源管理php

耗电量API

Android系统中很早就有耗电量的API,只不过一直都是隐藏的,Android系统的设置-电池功能就是调用的这个API,该API的核心部分是调用了com.android.internal.os.BatteryStatsHelper类,利用PowerProfile类,读取power_profile.xml文件,咱们一块儿来看看具体如何计算耗电量,首先从最新版本6.0开始看html

6.0的API

源码

BatteryStatsHelper 
其中计算耗电量的方法为490行的processAppUsage,下来一步一步来解释该方法。java

App耗电量的计算探究

private void processAppUsage(SparseArray<UserHandle> asUsers) {

方法的参数是一个SparseArray数组,存储的对象是UserHandle,官方文档给出的解释是,表明一个用户,能够理解为这个类里面存储了用户的相关信息.android

final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);

而后判断该次计算是否针对全部用户,经过UserHandleUSER_ALL值来判断,该值为-1,源码的地址在https://github.com/DoctorQ/platform_frameworks_base/blob/android-6.0.0_r1/core/java/android/os/UserHandle.java.git

mStatsPeriod = mTypeBatteryRealtime;

而后给公共变量int类型的mStatsPeriod赋值,这个值mTypeBatteryRealtime的计算过程又在320行的refreshStats方法中:github

mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);

 

这里面用到了BatteryStats(mStats)类中的computeBatteryRealtime方法,该方法计算出这次统计电量的时间间隔。好,歪楼了,回到BatteryStatsHelper中。api

BatterySipper osSipper = null; final SparseArray<? extends Uid> uidStats = mStats.getUidStats(); final int NU = uidStats.size();

 

首先建立一个BatterySipper对象osSipper,该对象里面能够存储一些后续咱们要计算的值,而后经过BatteryStats类对象mStats来获得一个包含Uid的对象的SparseArray组数,而后计算了一下这个数组的大小,保存在变量NU中。数组

for (int iu = 0; iu < NU; iu++) { final Uid u = uidStats.valueAt(iu); final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);

 

而后for循环计算每一个Uid表明的App的耗电量,由于BatterySipper可计算的类型有三种:应用, 系统服务, 硬件类型,因此这个地方传入的是DrainType.APP,还有其余可选类型以下:ruby

public enum DrainType { IDLE, CELL, PHONE, WIFI, BLUETOOTH, FLASHLIGHT, SCREEN, APP, USER, UNACCOUNTED, OVERCOUNTED, CAMERA }

列举了目前可计算耗电量的模块。markdown

mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);

 

其中mStatsType的值为BatteryStats.STATS_SINCE_CHARGED,表明了咱们的计算规则是从上次充满电后数据,还有一种规则是STATS_SINCE_UNPLUGGED是拔掉USB线后的数据。而mRawRealtime是当前时间,mRawUptime是运行时间。6.0的对各个模块的消耗都交给了单独的类去计算,这些类都继承于PowerCalculator抽象类:

蓝牙耗电:BluetoothPowerCalculator.java 摄像头耗电:CameraPowerCalculator.java Cpu耗电:CpuPowerCalculator.java 手电筒耗电:FlashlightPowerCalculator.java 无线电耗电:MobileRadioPowerCalculator.java 传感器耗电:SensorPowerCalculator.java Wakelock耗电:WakelockPowerCalculator.java Wifi耗电:WifiPowerCalculator.java

 

这一部分我一会单独拿出来挨个解释,如今咱们仍是回到BatteryStatsHelper继续往下走

final double totalPower = app.sumPower();

BatterySipper#sumPower方法是统计总耗电量,方法详情以下,其中usagePowerMah这个值有点特殊,其余的上面都讲过.

/** * Sum all the powers and store the value into `value`. * @return the sum of all the power in this BatterySipper. */ public double sumPower() { return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah + flashlightPowerMah; }

 

而后根据是不是DEBUG版本打印信息,这个没啥可说的,而后会把刚才计算的电量值添加到列表中:

// Add the app to the list if it is consuming power. if (totalPower != 0 || u.getUid() == 0) { // // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list. // final int uid = app.getUid(); final int userId = UserHandle.getUserId(uid); if (uid == Process.WIFI_UID) { mWifiSippers.add(app); } else if (uid == Process.BLUETOOTH_UID) { mBluetoothSippers.add(app); } else if (!forAllUsers && asUsers.get(userId) == null && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) { // We are told to just report this user's apps as one large entry. List<BatterySipper> list = mUserSippers.get(userId); if (list == null) { list = new ArrayList<>(); mUserSippers.put(userId, list); } list.add(app); } else { mUsageList.add(app); } if (uid == 0) { osSipper = app; } }

首先判断totalPower的值和当前uid号是否符合规则,规则为总耗电量不为0或者用户id为0.当uid代表为WIFI或者蓝牙时,添加到下面对应的列表中,通常状况下正常的应用咱们直接保存到下面的mUsageList中就行就行,可是也有一些例外:

/** * List of apps using power. */ private final List<BatterySipper> mUsageList = new ArrayList<>(); /** * List of apps using wifi power. */ private final List<BatterySipper> mWifiSippers = new ArrayList<>(); /** * List of apps using bluetooth power. */ private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();

 

若是咱们的系统是单用户系统,且当前的userId号不在咱们的统计范围内,且其进程id号是大于Process.FIRST_APPLICATION_UID(10000,系统分配给普通应用的其实id号),咱们就要将其存放到mUserSippers数组中,定义以下:

private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();

最后判断uid为0的话,表明是Android操做系统的耗电量,赋值给osSipper(494行定义)就能够了,这样一个app的计算就完成了,遍历部分就不说了,保存这个osSipper是为了最后一步计算:

if (osSipper != null) { // The device has probably been awake for longer than the screen on // time and application wake lock time would account for. Assign // this remainder to the OS, if possible. mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime, mRawUptime, mStatsType); osSipper.sumPower(); }

 

主流程咱们已经介绍完了,下面来看各个子模块耗电量的计算

Cpu耗电量

CpuPowerCalculator.java

Cpu的计算要用到PowerProfile类,该类主要是解析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">0.1</item> <!-- ~200mA --> <item name="screen.full">0.1</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">0.1</item> <!-- ~3mA --> <item name="wifi.active">0.1</item> <!-- WIFI data transfer, ~200mA --> <item name="wifi.scan">0.1</item> <!-- WIFI network scanning, ~100mA --> <item name="dsp.audio">0.1</item> <!-- ~10mA --> <item name="dsp.video">0.1</item> <!-- ~50mA --> <item name="camera.flashlight">0.1</item> <!-- Avg. power for camera flash, ~160mA --> <item name="camera.avg">0.1</item> <!-- Avg. power use of camera in standard usecases, ~550mA --> <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>400000</value> <!-- 400 MHz CPU speed --> </array> <!-- Current when CPU is idle --> <item name="cpu.idle">0.1</item> <!-- Current at each CPU speed, as per 'cpu.speeds' --> <array name="cpu.active"> <value>0.1</value> <!-- ~100mA --> </array> <!-- This is the battery capacity in mAh (measured at nominal voltage) --> <item name="battery.capacity">1000</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>

 

这个里面存储了Cpu(cpu.speeds)的主频等级,以及每一个主频每秒消耗的毫安(cpu.active),好,如今回到CpuPowerCalculator中,先来看构造方法

public CpuPowerCalculator(PowerProfile profile) { final int speedSteps = profile.getNumSpeedSteps(); mPowerCpuNormal = new double[speedSteps]; mSpeedStepTimes = new long[speedSteps]; for (int p = 0; p < speedSteps; p++) { mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); } }

 

第一步得到Cpu有几个主频等级,由于不一样等级消耗的电量不同,因此要区别对待,根据主频的个数,而后初始化mPowerCpuNormalmSpeedStepTimes,前者用来保存不一样等级的耗电速度,后者用来保存在不一样等级上耗时,而后给mPowerCpuNormal的每一个元素附上值。构造方法就完成了其全部的工做,如今来计算方法calculateApp,

final int speedSteps = mSpeedStepTimes.length; long totalTimeAtSpeeds = 0; for (int step = 0; step < speedSteps; step++) { mSpeedStepTimes[step] = u.getTimeAtCpuSpeed(step, statsType); totalTimeAtSpeeds += mSpeedStepTimes[step]; } totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); 

 

首先获得Cpu主频等级个数,而后BatteryStats.Uid获得不一样主频上执行时间,计算Cpu总耗时保存在totalTimeAtSpeeds中,

app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;

Cpu的执行时间分不少部分,可是咱们关注UserKernal部分,也就是上面的UserCpuTimeSystemCpuTime

double cpuPowerMaMs = 0; for (int step = 0; step < speedSteps; step++) { final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds; final double cpuSpeedStepPower = ratio * app.cpuTimeMs * mPowerCpuNormal[step]; if (DEBUG && ratio != 0) { Log.d(TAG, "UID " + u.getUid() + ": CPU step #" + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power=" + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); } cpuPowerMaMs += cpuSpeedStepPower; }

 

上面的代码就是将不一样主频的消耗累加到一块儿,可是其中值得注意的是,他并非用各个主频的消耗时间*主频单位时间内消耗的电量,而是用一个radio变量来计算获得各个主频段执行时间占总时间的百分比,而后用cpuTimeMs来换算成各个主频的Cpu实际消耗时间,这比5.0的API多了这么一步,我估计是发现了计算的不严谨性,这也是Android迟迟不放出统计电量方式的缘由,其实google本身对这块也没有把握,因此才会形成不一样API计算方式的差别。好,计算完咱们的总消耗后,是否是就算完事了?若是你只须要获得一个App的耗电总量,上面的讲解已经足够了,可是6.0的API计算了每一个App的不一样进程的耗电量,这个咱们就只当看看就行,暂时没什么实际意义。

// Keep track of the package with highest drain. double highestDrain = 0; app.cpuFgTimeMs = 0; final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); final int processStatsCount = processStats.size(); for (int i = 0; i < processStatsCount; i++) { final BatteryStats.Uid.Proc ps = processStats.valueAt(i); final String processName = processStats.keyAt(i); app.cpuFgTimeMs += ps.getForegroundTime(statsType); final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType) + ps.getForegroundTime(statsType); // Each App can have multiple packages and with multiple running processes. // Keep track of the package who's process has the highest drain. if (app.packageWithHighestDrain == null || app.packageWithHighestDrain.startsWith("*")) { highestDrain = costValue; app.packageWithHighestDrain = processName; } else if (highestDrain < costValue && !processName.startsWith("*")) { highestDrain = costValue; app.packageWithHighestDrain = processName; } } // Ensure that the CPU times make sense. if (app.cpuFgTimeMs > app.cpuTimeMs) { if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) { Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); } // Statistics may not have been gathered yet. app.cpuTimeMs = app.cpuFgTimeMs; }

 

上面统计同一App下不一样的进程的耗电量,获得消耗最大的进程名,保存到BatterySipper对象中,而后得出AppCpuforeground消耗时间,将foreground时间与以前计算获得的cpuTimeMs进行比较,若是foreground时间比cpuTimeMs还要大,那么就将cpuTimeMs的时间改变为foreground的值,可是这个值的变化对以前耗电总量的计算没有丝毫影响。

// Convert the CPU power to mAh app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);

 

最后的最后,将耗电量用mAh单位来表示,因此在毫秒的基础上除以60*60*1000

总结:Cpu耗电量的计算是要区分不一样主频的,频率不一样,单位时间内消耗的电量是有区分的,这一点要明白。还有一点就是不一样主频上的执行时间不是经过BatteryStats.Uid#getTimeAtCpuSpeed方法获得的,二十是经过百分比和BatteryStats.Uid#getUserCpuTimeUsgetSystemCpuTimeUs计算获得cpuTimeMs乘积获得的。最后一点就是,cpuTimeMs时间是会在计算完毕后进行比较,比较的对象是CPUforeground时间。

WakeLock耗电量的计算

WakelockPowerCalculator.java

从构造方法开始,

public WakelockPowerCalculator(PowerProfile profile) { mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); }

 

首先获得power_profile.xmlcpu.awake表示的值,保存在mPowerWakelock变量中。构造方法只作了这么点事,下面进入calculateApp方法。

@Override
    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { long wakeLockTimeUs = 0; final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); final int wakelockStatsCount = wakelockStats.size(); for (int i = 0; i < wakelockStatsCount; i++) { final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i); // Only care about partial wake locks since full wake locks // are canceled when the user turns the screen off. BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); if (timer != null) { wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType); } } app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis mTotalAppWakelockTimeMs += app.wakeLockTimeMs; // Add cost of holding a wake lock. app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60); if (DEBUG && app.wakeLockPowerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs + " power=" + BatteryStatsHelper.makemAh(app.wakeLockPowerMah)); } }

 

首先得到Wakelock的数量,而后逐个遍历获得每一个Wakelock对象,获得该对象后,获得BatteryStats.WAKE_TYPE_PARTIAL的唤醒时间,而后累加,其实wakelock有4种,为何只取partial的时间,具体代码google也没解释的很清楚,只是用一句注释打发了咱们。获得总时间后,就能够与构造方法中的单位时间waklock消耗电量相乘获得Wakelock消耗的总电量。

Wifi耗电量的计算

首先来看构造方法,来了解一下WIFI的耗电量计算用到了power_profile.xml中的哪些属性:

public WifiPowerCalculator(PowerProfile profile) {
        mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE); mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX); mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX); }

 

咱们去PowerProfile.java找到上面三个常量表明的属性:

public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle"; public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx"; public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";

 

知道对应的xml的属性后咱们直接看calculateApp方法:

@Override
    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME, statsType); final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType); final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType); app.wifiRunningTimeMs = idleTime + rxTime + txTime; app.wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) / (1000*60*60); mTotalAppPowerDrain += app.wifiPowerMah; app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, statsType); app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA, statsType); app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA, statsType); app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA, statsType); if (DEBUG && app.wifiPowerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" + txTime + "ms power=" + BatteryStatsHelper.makemAh(app.wifiPowerMah)); } }

这里的计算方式也是差很少,先根据Uid获得时间,而后乘以构造方法里对应的wifi类型单位时间内消耗电量值,没什么难点,就不一一分析,须要注意的是,这里面还计算了wifi传输的数据包的数量和字节数。

蓝牙耗电量的计算

蓝牙关注的power_profile.xml中的属性以下:

public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle"; public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx"; public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";

 

可是尚未单独为App计算耗电量的,因此这个地方是空的。

@Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // No per-app distribution yet. }

 

摄像头耗电量的计算

CameraPowerCalculator.java

摄像头的耗电量关注的是power_profile.xmlcamera.avg属性表明的值,保存到mCameraPowerOnAvg,

public static final String POWER_CAMERA = "camera.avg";

计算方式以下:

@Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // Calculate camera power usage. Right now, this is a (very) rough estimate based on the // average power usage for a typical camera application. final BatteryStats.Timer timer = u.getCameraTurnedOnTimer(); if (timer != null) { final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; app.cameraTimeMs = totalTime; app.cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60); } else { app.cameraTimeMs = 0; app.cameraPowerMah = 0; } }

 

先计算摄像头打开的时间totalTime,而后根据这个值乘以mCameraPowerOnAvg获得摄像头的耗电量。

手电筒耗电量的计算

FlashlightPowerCalculator.java

public static final String POWER_FLASHLIGHT = "camera.flashlight";

跟摄像头相似,也是先获得时间,而后乘积,不想说了,没意思。

无线电耗电量的计算

MobileRadioPowerCalculator.java

关注的是power_profile.xml中以下三个属性:

/** * Power consumption when screen is on, not including the backlight power. */ public static final String POWER_SCREEN_ON = "screen.on"; /** * Power consumption when cell radio is on but not on a call. */ public static final String POWER_RADIO_ON = "radio.on"; /** * Power consumption when cell radio is hunting for a signal. */ public static final String POWER_RADIO_SCANNING = "radio.scanning";

 

当无限量链接上时,根据信号强度不一样,耗电量的计算是有区别的,因此在构造方法,当无线电的状态为on时,是要特殊处理的,其余两个状态(active和scan)就正常取值就能够了。

/** * Power consumption when screen is on, not including the backlight power. */ public static final String POWER_SCREEN_ON = "screen.on"; /** * Power consumption when cell radio is on but not on a call. */ public static final String POWER_RADIO_ON = "radio.on"; /** * Power consumption when cell radio is hunting for a signal. */ public static final String POWER_RADIO_SCANNING = "radio.scanning";

 

计算的方式分两种,以无线电处于active状态的次数为区分,当active大于0,咱们用处于active状态的时间来乘以它的单位耗时。另外一种状况就要根据网络转化的数据包来计算耗电量了。

传感器耗电量的计算

SensorPowerCalculator.java

只关注一个属性:

public static final String POWER_GPS_ON = "gps.on";
  • 1

计算方式以下:

@Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // Process Sensor usage final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); final int NSE = sensorStats.size(); for (int ise = 0; ise < NSE; ise++) { final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise); final int sensorHandle = sensorStats.keyAt(ise); final BatteryStats.Timer timer = sensor.getSensorTime(); final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; switch (sensorHandle) { case BatteryStats.Uid.Sensor.GPS: app.gpsTimeMs = sensorTime; app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60); break; default: final int sensorsCount = mSensors.size(); for (int i = 0; i < sensorsCount; i++) { final Sensor s = mSensors.get(i); if (s.getHandle() == sensorHandle) { app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60); break; } } break; } } }

 

当传感器的类型为GPS时,咱们计算每一个传感器的时间而后乘以耗电量,和全部的耗电量计算都是同样,不一样的是,当传感器不是GPS时,这个时候计算就根据SensorManager获得全部传感器类型,这个里面保存有不一样传感器的单位耗电量,这样就能计算不一样传感器的耗电量。

总结

至此我已经把App耗电量的计算讲完了(还有硬件),先后花费3天时间,好痛苦(此处一万只草泥马),不过好在本身也算对这个耗电量的理解有了必定的认识。google官方对耗电量的统计给出的解释都是不能表明真实数据,只能做为参考值,由于受power_profile.xml的干扰太大,若是手机厂商没有严格设置这个文件,那可想而知出来的值多是不合理的。

提示

腾讯的GT团队前几天推出了耗电量的计算APK,原理是同样的,你们能够试用下GT

相关文章
相关标签/搜索