1.概要
Android中Settings->Battery->Battery usage中,可以查看电池电量使用情况。(充满电的状态下,各个应用消耗电量的实际情况)。
电池电量实际使用情况,其实是根据power_profile.xml中的配置有关,包括Screen亮灭屏、Wifi、Bluetooth等,通过计算app在一段时间内使用的相关资源的电量,估算出耗电量,并显示出来。
2.问题表现
进入Battery usage后,通过放电应用放电一定时间后,显示"没有电池使用数据"。
2.1.调查过程
通过Monitor获取当前布局,得到BatteryAppListPreferenceController.java(项目不同可能java类命名不同)。
在当前类中主要代码:
public boolean shouldShowBatteryAttributionList(Context context) { ...... PowerProfile powerProfile = new PowerProfile(context); // Cheap hack to try to figure out if the power_profile.xml was populated. final double averagePowerForOrdinal = powerProfile.getAveragePowerForOrdinal( PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0); final boolean shouldShowBatteryAttributionList = averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP; if (!shouldShowBatteryAttributionList) { Log.w(TAG, "shouldShowBatteryAttributionList(): " + averagePowerForOrdinal); } return shouldShowBatteryAttributionList; }
shouldShowBatteryAttributionList是判断电池使用数据是否显示的boolean值,实际上跟averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP 有关。
MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
averagePowerForOrdinal 则通过 POWER_GROUP_DISPLAY_SCREEN_FULL 得到。
因此,在PowerProfile.java中去找POWER_GROUP_DISPLAY_SCREEN_FULL的实际值:
在对应power_profile.xml的参数:
<!-- Display related values. --> <!-- Average battery current draw of display0 while in ambient mode, including backlight. There must be one of these for each display, labeled: ambient.on.display0, ambient.on.display1, etc... Each display suffix number should match it's ordinal in its display device config. --> <item name="ambient.on.display0">230.4</item> <!-- ~100mA --> <!-- Average battery current draw of display0 while on without backlight. There must be one of these for each display, labeled: screen.on.display0, screen.on.display1, etc... Each display suffix number should match it's ordinal in its display device config. --> <item name="screen.on.display0">196.4</item> <!-- ~100mA --> <!-- Average battery current draw of the backlight at full brightness. The full current draw of display N at full brightness should be the sum of screen.on.displayN and screen.full.displayN There must be one of these for each display, labeled: screen.full.display0, screen.full.display1, etc... Each display suffix number should match it's ordinal in its display device config. --> <item name="screen.full.display0">307.1</item> <!-- ~100mA --> <item name="bluetooth.active">157.7</item> <!-- Bluetooth data transfer, ~10mA -->
这里的screen.full.display0就是对应的POWER_GROUP_DISPLAY_SCREEN_FULL的值。如果设置的值小于10,则不会显示应用的耗电量:
public void refreshAppListGroup(BatteryUsageStats batteryUsageStats, boolean showAllApps) { if (!isAvailable()) { return; } mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats; mAppListGroup.setTitle(R.string.power_usage_list_summary); // 在后面会用到这个值,当前是false boolean addedSome = false; cacheRemoveAllPrefs(mAppListGroup); mAppListGroup.setOrderingAsAdded(false); // 如果之前的shouldShowBatteryAttributionList 为true,则进入此方法,正常计算并显示耗电量 if (sConfig.shouldShowBatteryAttributionList(mContext)) { final int dischargePercentage = getDischargePercentage(batteryUsageStats); final List<BatteryEntry> usageList = getCoalescedUsageList(showAllApps, /*loadDataInBackground=*/ true); final double totalPower = batteryUsageStats.getConsumedPower(); final int numSippers = usageList.size(); for (int i = 0; i < numSippers; i++) { final BatteryEntry entry = usageList.get(i); final double percentOfTotal = mBatteryUtils.calculateBatteryPercent( entry.getConsumedPower(), totalPower, dischargePercentage); if (((int) (percentOfTotal + .5)) < 1) { continue; } final int uid = entry.getUid(); final UserHandle userHandle = new UserHandle(UserHandle.getUserId(uid)); final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle); final CharSequence contentDescription = mUserManager.getBadgedLabelForUser( entry.getLabel(), userHandle); final String key = entry.getKey(); PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key); if (pref == null) { pref = new PowerGaugePreference(mPrefContext, badgedIcon, contentDescription, entry); pref.setKey(key); } entry.mPercent = percentOfTotal; pref.setTitle(entry.getLabel()); pref.setOrder(i + 1); pref.setPercent(percentOfTotal); pref.shouldShowAnomalyIcon(false); pref.setEnabled(uid != BatteryUtils.UID_TETHERING && uid != BatteryUtils.UID_REMOVED_APPS); setUsageSummary(pref, entry); addedSome = true; mAppListGroup.addPreference(pref); if (mAppListGroup.getPreferenceCount() - getCachedCount() > (MAX_ITEMS_TO_LIST + 1)) { break; } } } // 如果shouldShowBatteryAttributionList 为true,在上面的代码中,会将addedSome修改为 true,否则仍为false // “没有电池使用数据”显示的时候,shouldShowBatteryAttributionList为false,因此进入了如下的判断,调用了addNotAvailableMessage()方法。 if (!addedSome) { addNotAvailableMessage(); } removeCachedPrefs(mAppListGroup); BatteryEntry.startRequestQueue(); }
"没有电池使用数据"显示的时候,shouldShowBatteryAttributionList为false,因此进入了如下的判断,调用了addNotAvailableMessage()方法。我们来看看这个方法中主要做了什么:
private void addNotAvailableMessage() { Preference notAvailable = getCachedPreference(NOT_AVAILABLE); if (notAvailable == null) { notAvailable = new Preference(mPrefContext); notAvailable.setKey(NOT_AVAILABLE); // 就是这里 显示了 “没有电池使用数据” notAvailable.setTitle(R.string.power_usage_not_available); notAvailable.setSelectable(false); mAppListGroup.addPreference(notAvailable); } }
3.结论
因此,电池使用数据与frameworks/base/core/res/res/xml/power_profile.xml中的screen.full.display有着紧密关系。此外,power_profile.xml中的数据需要根据硬件测试提供的实际数据来进行调整,如果有对应的overlay目录,配置在对应的overlay目录下更好。
注:此为自己所在项目调查时的结论,与实际可能有偏差。不足之处请大佬们指出。