參考文章:Android應用的耗電量統計
深刻淺出Android App耗電量統計
Battery stats - CPU total vs CPU foreground
深刻淺出 Android App 耗電量統計
淺析Wakelock機制與Android電源管理php
Android
系統中很早就有耗電量的API
,只不過一直都是隱藏的,Android
系統的設置-電池功能
就是調用的這個API
,該API
的核心部分是調用了com.android.internal.os.BatteryStatsHelper
類,利用PowerProfile
類,讀取power_profile.xml
文件,咱們一塊兒來看看具體如何計算耗電量,首先從最新版本6.0開始看html
BatteryStatsHelper
其中計算耗電量的方法爲490行的processAppUsage
,下來一步一步來解釋該方法。java
private void processAppUsage(SparseArray<UserHandle> asUsers) {
方法的參數是一個SparseArray
數組,存儲的對象是UserHandle
,官方文檔給出的解釋是,表明一個用戶,能夠理解爲這個類裏面存儲了用戶的相關信息.android
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
而後判斷該次計算是否針對全部用戶,經過UserHandle
的USER_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的計算要用到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
有幾個主頻等級,由於不一樣等級消耗的電量不同,因此要區別對待,根據主頻的個數,而後初始化mPowerCpuNormal
和mSpeedStepTimes
,前者用來保存不一樣等級的耗電速度,後者用來保存在不一樣等級上耗時,而後給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
的執行時間分不少部分,可是咱們關注User
和Kernal
部分,也就是上面的UserCpuTime
和SystemCpuTime
。
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
對象中,而後得出App
的Cpu
的foreground
消耗時間,將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#getUserCpuTimeUs
和getSystemCpuTimeUs
計算獲得cpuTimeMs
乘積獲得的。最後一點就是,cpuTimeMs
時間是會在計算完畢後進行比較,比較的對象是CPU
的foreground
時間。
從構造方法開始,
public WakelockPowerCalculator(PowerProfile profile) { mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); }
首先獲得power_profile.xml
中cpu.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的耗電量計算用到了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. }
攝像頭的耗電量關注的是power_profile.xml
中camera.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
狀態的時間來乘以它的單位耗時。另外一種狀況就要根據網絡轉化的數據包來計算耗電量了。
只關注一個屬性:
public static final String POWER_GPS_ON = "gps.on";
計算方式以下:
@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