Android BatteryStatsHelper深入理解(and5.1)
2015-08-04 14:47
429 查看
BatteryStatsHelper这个类主要是统计各个应用,多用户的每个用户,以及蓝牙,屏幕等耗电统计。
一般使用BatteryStatsHelper这个类,先要new一个实例,然后再调用create函数:
下面我们就先从create分析,两种create方法,其中sStatsXfer是静态的
mPowerProfile是从power_profile.xml读取的各个器件的电源消耗参数,文件如下:
接下来说下refreshStats是用来更新电池最新状态的,statsType是指充电状态还是非充电状态,asUsers指的是userId(多用户)
接下来分析下refreshStats函数
下面先看processAppUsage函数,这个函数是看uid的耗电信息。
processMiscUsage函数是统计屏幕、wifi耗电等等
addEntry函数,新建一个BatterySipper,加入mUsageList
而如果应用想要获取各个耗电信息,先要refreshStats,再去获取getUsageList,下面是settings中电池那一栏里面的代码
获取后可以根据BatterySipper的value获取每小时的耗电量.
下面我们分析下settings,电池里面的refreshStats函数:
碰到一个问题当电池电量到90%,拔去usb线,settings会显示“没有电池使用数据”,带着这个问题,我们再来看看BatteryStatsImpl:
我们先来看看BatteryStatsHelper中refreshStats函数中的一段代码,如何统计mTotalPower,先会计算一个mComputedPower,会把每个usage的power累计相加,然后下面还有一段代码,mStats.getLowDischargeAmountSinceCharge是什么意思呢?带着这个问题再去看BatteryStatsImpl的代码。
先看BatteryStatsImpl中getLowDischargeAmountSinceCharge函数,直接从代码中找哪里改变了这个变量mLowDischargeAmountSinceCharge
这两个变量中,这两个变量差1,就是有一个误差的范围而已。
现在我们再来看看getLowDischargeAmountSinceCharge函数。
最近一次的消耗电量加上之前累计的消耗电量就是总的消耗电量了。
下面看看几个关键变量
再回到前面一开始的BatteryStatsHelper中的mTotalPower问题,
再看看mMinDrainedPower 和mMaxDrainedPower 两个变量,就是消耗电量除以100,乘以总电量就是消耗的实际电量而不是百分比了。
回过头来看看settings为什么90%之后,拔掉usb线就没有电池数据了,settings会遍历所有的usagelist,只有所有的都continue才会出现没有电池数据,一个可能及时dischargeAmount为0,mStatsHelper.getTotalPower,和sipper.value
而我们看看BatteryStatsImpl中getDischargeAmount就是getHighDischargeAmountSinceCharge函数,而getHighDischargeAmountSinceCharge就是统计了电池消耗的电量,只是范围比较大的那个值,所有到90%没有关系。
mStatsHelper.getTotalPower也是BatteryStatsHelper统计的消耗电量,和到90%也没有关系。
那么剩下只有一个可能,sipper.value 为0. 而每个usage的消耗电量为0,只有是统计的时间为0.
再去BatteryStatsImpl看看什么时候会将统计的时间置为0.
在resetAllStatsLocked接口里会把所有的timer重新reset,之后看看谁调用了这个函数
一般使用BatteryStatsHelper这个类,先要new一个实例,然后再调用create函数:
下面我们就先从create分析,两种create方法,其中sStatsXfer是静态的
public void create(BatteryStats stats) { mPowerProfile = new PowerProfile(mContext); mStats = stats; } public void create(Bundle icicle) { if (icicle != null) { mStats = sStatsXfer; mBatteryBroadcast = sBatteryBroadcastXfer; } mBatteryInfo = IBatteryStats.Stub.asInterface( ServiceManager.getService(BatteryStats.SERVICE_NAME)); mPowerProfile = new PowerProfile(mContext); }
mPowerProfile是从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">100</item> <!-- ~200mA --> <item name="screen.full">200</item> <!-- ~300mA --> <item name="bluetooth.active">90.5</item> <!-- Bluetooth data transfer, ~10mA --> <item name="bluetooth.on">2.5</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA --> <item name="wifi.on">1.25</item> <!-- ~3mA --> <item name="wifi.active">130</item> <!-- WIFI data transfer, ~200mA --> <item name="wifi.scan">100</item> <!-- WIFI network scanning, ~100mA --> <item name="dsp.audio">30.5</item> <!-- ~10mA --> <item name="dsp.video">72.5</item> <!-- ~50mA --> <item name="radio.active">135</item> <!-- ~200mA --> <item name="radio.scanning">5.3</item> <!-- cellular radio scanning for signal, ~10mA --> <item name="gps.on">30</item> <!-- ~50mA --> <!-- Current consumed by the radio at different signal strengths, when paging --> <array name="radio.on"> <!-- Strength 0 to BINS-1 --> <value>3.5</value> <!-- ~2mA --> <value>2.4</value> <!-- ~1mA --> </array> <!-- Different CPU speeds as reported in /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state --> <array name="cpu.speeds"> <value>624000</value> <!-- 624 MHz CPU speed --> <value>699563</value> <!-- 699 MHz CPU speed --> <value>799500</value> <!-- 799 MHz CPU speed --> <value>899438</value> <!-- 899 MHz CPU speed --> <value>999375</value> <!-- 999 MHz CPU speed --> <value>1099313</value> <!-- 1099 MHz CPU speed --> <value>1199250</value> <!-- 1199 MHz CPU speed --> <value>1299188</value> <!-- 1299 MHz CPU speed --> <value>1399125</value> <!-- 1399 MHz CPU speed --> <value>1499063</value> <!-- 1499 MHz CPU speed --> <value>1599000</value> <!-- 1599 MHz CPU speed --> </array> <!-- Current when CPU is idle --> <item name="cpu.idle">2.2</item> <!-- Current at each CPU speed, as per 'cpu.speeds' --> <array name="cpu.active">//各个cpu频段的功耗 <value>54</value> <value>63</value> <value>72</value> <value>80</value> <value>90</value> <value>100</value> <value>109</value> <value>115</value> <value>121</value> <value>127</value> <value>135</value> </array> <!-- This is the battery capacity in mAh (measured at nominal voltage) --> <item name="battery.capacity">2000</item> </device>
接下来说下refreshStats是用来更新电池最新状态的,statsType是指充电状态还是非充电状态,asUsers指的是userId(多用户)
public void refreshStats(int statsType, List<UserHandle> asUsers) { final int n = asUsers.size(); SparseArray<UserHandle> users = new SparseArray<UserHandle>(n); for (int i = 0; i < n; ++i) { UserHandle userHandle = asUsers.get(i); users.put(userHandle.getIdentifier(), userHandle); } refreshStats(statsType, users); } /** * Refreshes the power usage list. */ public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) { refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000, SystemClock.uptimeMillis() * 1000); }
接下来分析下refreshStats函数
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs) { // Initialize mStats if necessary. getStats(); mMaxPower = 0; mMaxRealPower = 0; mComputedPower = 0; mTotalPower = 0; mWifiPower = 0; mBluetoothPower = 0; mAppMobileActive = 0; mAppWifiRunning = 0; mUsageList.clear(); mWifiSippers.clear(); mBluetoothSippers.clear(); mUserSippers.clear(); mUserPower.clear(); mMobilemsppList.clear(); if (mStats == null) { return; } mStatsType = statsType; mRawUptime = rawUptimeUs; mRawRealtime = rawRealtimeUs; mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs); mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs); mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);//获取电池剩余时间 mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);//获取充电剩余时间 if (DEBUG) { Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime=" + (rawUptimeUs/1000)); Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime=" + (mBatteryUptime/1000)); Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime=" + (mTypeBatteryUptime/1000)); } mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; processAppUsage(asUsers);//计算每个uid的耗电情况 // Before aggregating apps in to users, collect all apps to sort by their ms per packet. for (int i=0; i<mUsageList.size(); i++) { BatterySipper bs = mUsageList.get(i); bs.computeMobilemspp(); if (bs.mobilemspp != 0) { mMobilemsppList.add(bs); } } for (int i=0; i<mUserSippers.size(); i++) { List<BatterySipper> user = mUserSippers.valueAt(i); for (int j=0; j<user.size(); j++) { BatterySipper bs = user.get(j); bs.computeMobilemspp(); if (bs.mobilemspp != 0) { mMobilemsppList.add(bs); } } } Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() { @Override public int compare(BatterySipper lhs, BatterySipper rhs) { if (lhs.mobilemspp < rhs.mobilemspp) { return 1; } else if (lhs.mobilemspp > rhs.mobilemspp) { return -1; } return 0; } }); processMiscUsage();//计算比如屏幕、wifi、蓝牙等耗电 if (DEBUG) { Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); } mTotalPower = mComputedPower; if (mStats.getLowDischargeAmountSinceCharge() > 1) { if (mMinDrainedPower > mComputedPower) { double amount = mMinDrainedPower - mComputedPower; mTotalPower = mMinDrainedPower; addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);//加一个未统计电量 } else if (mMaxDrainedPower < mComputedPower) {// double amount = mComputedPower - mMaxDrainedPower; addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);//加一个over统计 } } Collections.sort(mUsageList); }
下面先看processAppUsage函数,这个函数是看uid的耗电信息。
private void processAppUsage(SparseArray<UserHandle> asUsers) { final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); ............ SparseArray<? extends Uid> uidStats = mStats.getUidStats(); final int NU = uidStats.size(); for (int iu = 0; iu < NU; iu++) {//遍历各个uid Uid u = uidStats.valueAt(iu); double p; // in mAs double power = 0; // in mAs double highestDrain = 0; String packageWithHighestDrain = null; Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); long cpuTime = 0; long cpuFgTime = 0; long wakelockTime = 0; long gpsTime = 0; .............. if (cpuFgTime > cpuTime) {//先就算每个uid的cpu耗电 if (DEBUG && cpuFgTime > cpuTime + 10000) { Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); } cpuTime = cpuFgTime; // Statistics may not have been gathered yet. } power /= (60*60*1000); // Process wake lock usage Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry : wakelockStats.entrySet()) { Uid.Wakelock wakelock = wakelockEntry.getValue(); // 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) { wakelockTime += timer.getTotalTimeLocked(mRawRealtime, which); } } appWakelockTimeUs += wakelockTime; wakelockTime /= 1000; // convert to millis // Add cost of holding a wake lock//计算uid的wakelock耗电 p = (wakelockTime * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000); if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake " + wakelockTime + " power=" + makemAh(p)); power += p; // Add cost of mobile traffic//计算uid移动数据耗电 ................... if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets " + (mobileRx+mobileTx) + " active time " + mobileActive + " power=" + makemAh(p)); power += p; // Add cost of wifi traffic//计算wifi数据耗电 final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType); final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType); final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType); final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType); p = (wifiRx + wifiTx) * wifiPowerPerPacket; if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets " + (mobileRx+mobileTx) + " power=" + makemAh(p)); power += p; // Add cost of keeping WIFI running. long wifiRunningTimeMs = u.getWifiRunningTime(mRawRealtime, which) / 1000; mAppWifiRunning += wifiRunningTimeMs; p = (wifiRunningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000); if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running " + wifiRunningTimeMs + " power=" + makemAh(p)); power += p; // Add cost of WIFI scans long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000; p = (wifiScanTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000); if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs + " power=" + makemAh(p)); power += p; for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000; p = ((batchScanTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin)) ) / (60*60*1000); if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin + " time=" + batchScanTimeMs + " power=" + makemAh(p)); power += p; } // Process Sensor usage//uid传感器耗电 ................... p = (multiplier * sensorTime) / (60*60*1000); if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle + " time=" + sensorTime + " power=" + makemAh(p)); power += p; } if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(), makemAh(power))); // Add the app to the list if it is consuming power final int userId = UserHandle.getUserId(u.getUid()); if (power != 0 || u.getUid() == 0) {//新建一个BatterySipper,BatterySipper 就是耗电的各个参数 BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, new double[] {power}); app.cpuTime = cpuTime; app.gpsTime = gpsTime; app.wifiRunningTime = wifiRunningTimeMs; app.cpuFgTime = cpuFgTime; app.wakeLockTime = wakelockTime; app.mobileRxPackets = mobileRx; app.mobileTxPackets = mobileTx; app.mobileActive = mobileActive / 1000; app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType); app.wifiRxPackets = wifiRx; app.wifiTxPackets = wifiTx; app.mobileRxBytes = mobileRxB; app.mobileTxBytes = mobileTxB; app.wifiRxBytes = wifiRxB; app.wifiTxBytes = wifiTxB; app.packageWithHighestDrain = packageWithHighestDrain; if (u.getUid() == Process.WIFI_UID) {//如果uid是wifi的,就加到mWifiSippers mWifiSippers.add(app); mWifiPower += power; } else if (u.getUid() == Process.BLUETOOTH_UID) {//蓝牙的就加到mBluetoothSippers mBluetoothSippers.add(app); mBluetoothPower += power; } else if (!forAllUsers && asUsers.get(userId) == null//如果没有这个userId(多用户),就重新建立一个 && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) { List<BatterySipper> list = mUserSippers.get(userId); if (list == null) { list = new ArrayList<BatterySipper>(); mUserSippers.put(userId, list); } list.add(app); if (power != 0) { Double userPower = mUserPower.get(userId); if (userPower == null) { userPower = power; } else { userPower += power; } mUserPower.put(userId, userPower); } } else {//或者直接加到mUsageList,mUsageList管理所有的耗电信息list mUsageList.add(app); if (power > mMaxPower) mMaxPower = power; if (power > mMaxRealPower) mMaxRealPower = power; mComputedPower += power; } if (u.getUid() == 0) { osApp = app; } } }
processMiscUsage函数是统计屏幕、wifi耗电等等
private void processMiscUsage() { addUserUsage(); addPhoneUsage(); addScreenUsage(); addFlashlightUsage(); addWiFiUsage(); addBluetoothUsage(); addIdleUsage(); // Not including cellular idle power // Don't compute radio usage if it's a wifi-only device if (!mWifiOnly) { addRadioUsage(); } }
private void addScreenUsage() { double power = 0; long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000; power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);//时间乘以平均功耗 final double screenFullPower = mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { double screenBinPower = screenFullPower * (i + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType) / 1000; double p = screenBinPower*brightnessTime; if (DEBUG && p != 0) { Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime + " power=" + makemAh(p / (60 * 60 * 1000))); } power += p; } power /= (60*60*1000); // To hours最后再要除以1小时 if (power != 0) { addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); } }
addEntry函数,新建一个BatterySipper,加入mUsageList
private BatterySipper addEntry(DrainType drainType, long time, double power) { mComputedPower += power;//计算所有的power if (power > mMaxRealPower) mMaxRealPower = power; return addEntryNoTotal(drainType, time, power); } private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) { if (power > mMaxPower) mMaxPower = power; BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});//新建一个BatterySipper,加入mUsageList bs.usageTime = time; mUsageList.add(bs); return bs; }
而如果应用想要获取各个耗电信息,先要refreshStats,再去获取getUsageList,下面是settings中电池那一栏里面的代码
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles); final List<BatterySipper> usageList = mStatsHelper.getUsageList();
获取后可以根据BatterySipper的value获取每小时的耗电量.
下面我们分析下settings,电池里面的refreshStats函数:
private void refreshStats() {
mAppListGroup.removeAll();
mAppListGroup.setOrderingAsAdded(false);
mHistPref = new BatteryHistoryPreference(getActivity(), mStatsHelper.getStats(),
mStatsHelper.getBatteryBroadcast());
mHistPref.setOrder(-1);
mAppListGroup.addPreference(mHistPref);
boolean addedSome = false;
final PowerProfile powerProfile = mStatsHelper.getPowerProfile();//每个器件的平均耗电
final BatteryStats stats = mStatsHelper.getStats();
final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
if (averagePower >= MIN_***ERAGE_POWER_THRESHOLD_MILLI_AMP) {//如果没有配置原生的power_profile.xml,这里的averagePower 为0.1
Log.e("TAG", "enter averagePower >= MIN_***ERAGE_POWER_THRESHOLD_MILLI_AMP");
final List<UserHandle> profiles = mUm.getUserProfiles();
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles); final List<BatterySipper> usageList = mStatsHelper.getUsageList();//获取各个耗电
final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0;//BatteryStatsService统计各个的时间会在usb线拔掉后清零,dichargeaAmount相当于usb拔掉后这段时间的电量,相当于电池使用的量
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
if ((sipper.value * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {//BatterySipper 的power都是以每小时计的
continue;
}
final double percentOfTotal =
((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount);//和上面一样(sipper.value / mStatsHelper.getTotalPower()统计的值,代表是该统计占该段时间内总的消耗电量的一个比,而该段时间是就是BatteryStats统计的时间,因此这段时间统计的电量还要乘以一个,这段时间内消耗的电量比上总电量的一个值。而且这个消耗电量的统计在BatteryStatsImpl中,当充电时,会把该项消耗电量统计下。
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {
// Don't show over-counted unless it is at least 2/3 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {
continue;
}
if (percentOfTotal < 10) {
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {
// Don't show over-counted unless it is at least 1/2 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {
continue;
}
if (percentOfTotal < 5) {
Log.e("kangkang", "7");
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),
userHandle);
final PowerGaugePreference pref = new PowerGaugePreference(getActivity(),
badgedIcon, contentDescription, entry);
final double percentOfMax = (sipper.value * 100) / mStatsHelper.getMaxPower();
sipper.percent = percentOfTotal;
pref.setTitle(entry.getLabel());
pref.setOrder(i + 1);
pref.setPercent(percentOfMax, percentOfTotal);
if (sipper.uidObj != null) {
pref.setKey(Integer.toString(sipper.uidObj.getUid()));
}
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
}
if (!addedSome) {//如果走到这里会显示"没有电池数据",要么power_profile.xml没有配置,要么每项都continue
addNotAvailableMessage();
}
BatteryEntry.startRequestQueue();
}
碰到一个问题当电池电量到90%,拔去usb线,settings会显示“没有电池使用数据”,带着这个问题,我们再来看看BatteryStatsImpl:
我们先来看看BatteryStatsHelper中refreshStats函数中的一段代码,如何统计mTotalPower,先会计算一个mComputedPower,会把每个usage的power累计相加,然后下面还有一段代码,mStats.getLowDischargeAmountSinceCharge是什么意思呢?带着这个问题再去看BatteryStatsImpl的代码。
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs) { 。。。。。。。。。。。。 mTotalPower = mComputedPower; if (mStats.getLowDischargeAmountSinceCharge() > 1) {// if (mMinDrainedPower > mComputedPower) { double amount = mMinDrainedPower - mComputedPower; mTotalPower = mMinDrainedPower; addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount); } else if (mMaxDrainedPower < mComputedPower) { double amount = mComputedPower - mMaxDrainedPower; addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount); } } Collections.sort(mUsageList); }
先看BatteryStatsImpl中getLowDischargeAmountSinceCharge函数,直接从代码中找哪里改变了这个变量mLowDischargeAmountSinceCharge
@Override public int getLowDischargeAmountSinceCharge() { synchronized(this) { int val = mLowDischargeAmountSinceCharge; if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) { val += mDischargeUnplugLevel-mDischargeCurrentLevel-1; } return val; } }发现在setOnBatteryLocked函数中会改变其值,而setOnBatteryLocked函数是有BatteryService调用,只有当充电方式改变才会掉,而我们这段代码代表,只有插上usb线会调用,level代表当前电量,mDischargeUnplugLevel代表上一次拔去usb线的电量,这就代表上次拔去usb线后,电量level减少了,就是耗电了,在这次充电的时候把之前的耗电统计下,放在mLowDischargeAmountSinceCharge 和mHighDischargeAmountSinceCharge
这两个变量中,这两个变量差1,就是有一个误差的范围而已。
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level) { else { mOnBattery = mOnBatteryInternal = onBattery; pullPendingStateUpdatesLocked(); mHistoryCur.batteryLevel = (byte)level; mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(mSecRealtime, mSecUptime); mDischargeCurrentLevel = mDischargePlugLevel = level; if (level < mDischargeUnplugLevel) {//level代表当前电量,mDischargeUnplugLevel代表上一次拔去usb线的电量 mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;//累计消耗的电量 mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level; } updateDischargeScreenLevelsLocked(screenOn, screenOn); updateTimeBasesLocked(false, !screenOn, uptime, realtime); mNumChargeStepDurations = 0; mLastChargeStepLevel = level; mMaxChargeStepLevel = level; mLastChargeStepTime = -1; mInitStepMode = mCurStepMode; mModStepMode = 0; }
现在我们再来看看getLowDischargeAmountSinceCharge函数。
@Override public int getLowDischargeAmountSinceCharge() { synchronized(this) { int val = mLowDischargeAmountSinceCharge; if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {//表示现在正在用电状态,mDischargeCurrentLevel 变量代表用电的时候的电量,会时时更新,mDischargeUnplugLevel代表上一次拔去usb线的一个电量,也就是最近的一次消耗电量 val += mDischargeUnplugLevel-mDischargeCurrentLevel-1; } return val; } }
最近一次的消耗电量加上之前累计的消耗电量就是总的消耗电量了。
下面看看几个关键变量
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level) { boolean doWrite = false; Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE); m.arg1 = onBattery ? 1 : 0; mHandler.sendMessage(m); final long uptime = mSecUptime * 1000; final long realtime = mSecRealtime * 1000; final boolean screenOn = mScreenState == Display.STATE_ON; if (onBattery) { 。。。。。。。。。。 addHistoryRecordLocked(mSecRealtime, mSecUptime); mDischargeCurrentLevel = mDischargeUnplugLevel = level;//刚拔去usb线的时候统计下mDischargeCurrentLevel和mDischargeUnplugLevel值 if (screenOn) { mDischargeScreenOnUnplugLevel = level; mDischargeScreenOffUnplugLevel = 0; } else { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenOffUnplugLevel = level; } mDischargeAmountScreenOn = 0; mDischargeAmountScreenOff = 0; updateTimeBasesLocked(true, !screenOn, uptime, realtime); } else { mOnBattery = mOnBatteryInternal = onBattery; pullPendingStateUpdatesLocked(); mHistoryCur.batteryLevel = (byte)level; mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(mSecRealtime, mSecUptime); mDischargeCurrentLevel = mDischargePlugLevel = level;//刚插上usb线的时候统计下mDischargeCurrentLevel和mDischargePlugLevel值setBatteryState是BatteryService时时调的,而setOnBatteryLocked只有充电方式改变后才会调用。
public void setBatteryState(int status, int health, int plugType, int level, int temp, int volt) { synchronized(this) { final boolean onBattery = plugType == BATTERY_PLUGGED_NONE; final long uptime = SystemClock.uptimeMillis(); final long elapsedRealtime = SystemClock.elapsedRealtime(); int oldStatus = mHistoryCur.batteryStatus; if (!mHaveBatteryLevel) { mHaveBatteryLevel = true; // We start out assuming that the device is plugged in (not // on battery). If our first report is now that we are indeed // plugged in, then twiddle our state to correctly reflect that // since we won't be going through the full setOnBattery(). if (onBattery == mOnBattery) { if (onBattery) { mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; } else { mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; } } oldStatus = status; } if (onBattery) { mDischargeCurrentLevel = level;//只要使用电池该mDischargeCurrentLevel 也是时时更新。 if (!mRecordingHistory) { mRecordingHistory = true; startRecordingHistory(elapsedRealtime, uptime, true); } } else if (level < 96) { if (!mRecordingHistory) { mRecordingHistory = true; startRecordingHistory(elapsedRealtime, uptime, true); } }
再回到前面一开始的BatteryStatsHelper中的mTotalPower问题,
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs) { 。。。。。。。。。。。。 mTotalPower = mComputedPower; if (mStats.getLowDischargeAmountSinceCharge() > 1) {//只要统计消耗的电量大于1 if (mMinDrainedPower > mComputedPower) {//如果实际总的消耗电量比统计的电量大,那么mTotalPower 就用实际消耗的电量 double amount = mMinDrainedPower - mComputedPower; mTotalPower = mMinDrainedPower; addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);//并且将没有统计到的电量,也放在usgaelist中 } else if (mMaxDrainedPower < mComputedPower) { double amount = mComputedPower - mMaxDrainedPower; addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount); } } Collections.sort(mUsageList); }
再看看mMinDrainedPower 和mMaxDrainedPower 两个变量,就是消耗电量除以100,乘以总电量就是消耗的实际电量而不是百分比了。
mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100;
回过头来看看settings为什么90%之后,拔掉usb线就没有电池数据了,settings会遍历所有的usagelist,只有所有的都continue才会出现没有电池数据,一个可能及时dischargeAmount为0,mStatsHelper.getTotalPower,和sipper.value
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles); final List<BatterySipper> usageList = mStatsHelper.getUsageList();
final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0;
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
if ((sipper.value * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {
continue;
}
final double percentOfTotal =
((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {
// Don't show over-counted unless it is at least 2/3 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {
continue;
}
if (percentOfTotal < 10) {
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {
// Don't show over-counted unless it is at least 1/2 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {
continue;
}
if (percentOfTotal < 5) {
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
而我们看看BatteryStatsImpl中getDischargeAmount就是getHighDischargeAmountSinceCharge函数,而getHighDischargeAmountSinceCharge就是统计了电池消耗的电量,只是范围比较大的那个值,所有到90%没有关系。
@Override public int getDischargeAmount(int which) { int dischargeAmount = which == STATS_SINCE_CHARGED ? getHighDischargeAmountSinceCharge() : (getDischargeStartLevel() - getDischargeCurrentLevel()); if (dischargeAmount < 0) { dischargeAmount = 0; } return dischargeAmount; }
mStatsHelper.getTotalPower也是BatteryStatsHelper统计的消耗电量,和到90%也没有关系。
那么剩下只有一个可能,sipper.value 为0. 而每个usage的消耗电量为0,只有是统计的时间为0.
再去BatteryStatsImpl看看什么时候会将统计的时间置为0.
在resetAllStatsLocked接口里会把所有的timer重新reset,之后看看谁调用了这个函数
private void resetAllStatsLocked() { mStartCount = 0; initTimes(SystemClock.uptimeMillis() * 1000, SystemClock.elapsedRealtime() * 1000); mScreenOnTimer.reset(false); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].reset(false); } mInteractiveTimer.reset(false); mLowPowerModeEnabledTimer.reset(false); mPhoneOnTimer.reset(false); mAudioOnTimer.reset(false); mVideoOnTimer.reset(false); mFlashlightOnTimer.reset(false); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].reset(false); } mPhoneSignalScanningTimer.reset(false); for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) { mPhoneDataConnectionsTimer[i].reset(false); }结果就发现在setOnBatteryLocked
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level) { boolean doWrite = false; Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE); m.arg1 = onBattery ? 1 : 0; mHandler.sendMessage(m); final long uptime = mSecUptime * 1000; final long realtime = mSecRealtime * 1000; final boolean screenOn = mScreenState == Display.STATE_ON; if (onBattery) { // We will reset our status if we are unplugging after the // battery was last full, or the level is at 100, or // we have gone through a significant charge (from a very low // level to a now very high level). boolean reset = false; if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL || level >= 90//当当前电量大于90% || (mDischargeCurrentLevel < 20 && level >= 80)//当上次插电时的电量是20,现在充电充到80 || (getHighDischargeAmountSinceCharge() >= 200 && mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER))) { Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus + " dischargeLevel=" + mDischargeCurrentLevel + " lowAmount=" + getLowDischargeAmountSinceCharge() + " highAmount=" + getHighDischargeAmountSinceCharge()); // Before we write, collect a snapshot of the final aggregated // stats to be reported in the next checkin. Only do this if we have // a sufficient amount of data to make it interesting. if (getLowDischargeAmountSinceCharge() >= 20) { final Parcel parcel = Parcel.obtain(); writeSummaryToParcel(parcel, true); BackgroundThread.getHandler().post(new Runnable() { @Override public void run() { synchronized (mCheckinFile) { FileOutputStream stream = null; try { stream = mCheckinFile.startWrite(); stream.write(parcel.marshall()); stream.flush(); FileUtils.sync(stream); stream.close(); mCheckinFile.finishWrite(stream); } catch (IOException e) { Slog.w("BatteryStats", "Error writing checkin battery statistics", e); mCheckinFile.failWrite(stream); } finally { parcel.recycle(); } } } }); } doWrite = true; resetAllStatsLocked();//会将所有的timer,重置 mDischargeStartLevel = level; reset = true; mNumDischargeStepDurations = 0; }好了,现在有答案了,当电量大于90的时候,BatteryStatsImpl会将所有timer重置,导致BatteryStatsHelper的各个Usage统计的电量为0,之后Settings就会在遍历每个usage的时候continue,然后就会显示没有电池使用数据了。
相关文章推荐
- Android 电量监控、关机、重启功能的实现
- Android 自定义ViewGroup 实现FlowLayout,动态添加布局
- android登陆
- Android 扩大view点击范围
- Android实现抽奖转盘
- Android 存储学习之SQLite数据库的基本操作
- android相关报错之<Wrong orientation? No orientation specified, and the default is horizontal。。
- Android本地消息推送
- android强迫症头像BadgeView
- 推荐4个Android引导页控件
- 推荐3个Android筛选器控件
- android平台,利用ffmpeg对android摄像头采集编码
- 百度——地图API——Android—应用集成离线地图
- android压力测试工具monkey
- 总结Android Studio导入项目的几种方法
- Android开发:自己开发的一款通用弹窗库
- Android目录结构
- Mahone的CTS研究笔记(二):如何运行CTS
- Android实现4.4以上系统状态栏透明
- Android源码IInterface.IBinder,Parcelable的注释翻译