Android(安卓)Q 电量使用图分析 show app usage
这个界面就是packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageAdcanced.java,然后查看它加载的布局文件
@Override protected int getPreferenceScreenResId() { return R.xml.power_usage_advanced; }
布局代码如下
从布局上看,我们可以发现,我们要找的那个曲线图就是BatteryHistoryPreference,回到PowerUsageAdvanced中看一下
@Override public void onCreate(Bundle icicle) { super.onCreate(icicle); final Context context = getContext(); mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH); mPowerUsageFeatureProvider = FeatureFactory.getFactory(context) .getPowerUsageFeatureProvider(context); mBatteryUtils = BatteryUtils.getInstance(context); // init the summary so other preferences won't have unnecessary move updateHistPrefSummary(context); restoreSavedInstance(icicle); } @Override protected void refreshUi(@BatteryUpdateType int refreshType) { final Context context = getContext(); if (context == null) { return; } updatePreference(mHistPref); updateHistPrefSummary(context); mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps); } private void updateHistPrefSummary(Context context) { Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); final boolean plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0; if (mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context) && !plugged) { mHistPref.setBottomSummary( mPowerUsageFeatureProvider.getAdvancedUsageScreenInfoString()); } else { mHistPref.hideBottomSummary(); } }
我们发现BatteryHistoryPreference没有做什么特别的时候,只有一个updatePreference(),下面我们追一下看看去做什么了。这个updatePreference()在父类中有实现。
看一下packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageBase.java,很简单,就是调用了BatteryHistoryPreference中的setStats()。
protected void updatePreference(BatteryHistoryPreference historyPref) { final long startTime = System.currentTimeMillis(); historyPref.setStats(mStatsHelper); BatteryUtils.logRuntime(TAG, "updatePreference", startTime); }
然后我们看一下BatteryHistoryPreference,setStats就是去获取一下batteryinfo,主要看onBindViewHolder,它会调用BatteryInfo的bindHistory去画图。
public void setStats(BatteryStatsHelper batteryStats) { BatteryInfo.getBatteryInfo(getContext(), info -> { mBatteryInfo = info; notifyChanged(); }, batteryStats, false); } @Override public void onBindViewHolder(PreferenceViewHolder view) { super.onBindViewHolder(view); final long startTime = System.currentTimeMillis(); if (mBatteryInfo == null) { return; } ((TextView) view.findViewById(R.id.charge)).setText(mBatteryInfo.batteryPercentString); mSummaryView = (TextView) view.findViewById(R.id.bottom_summary); if (mSummary != null) { mSummaryView.setText(mSummary); } if (hideSummary) { mSummaryView.setVisibility(View.GONE); } UsageView usageView = (UsageView) view.findViewById(R.id.battery_usage); usageView.findViewById(R.id.label_group).setAlpha(.7f); mBatteryInfo.bindHistory(usageView); BatteryUtils.logRuntime(TAG, "onBindViewHolder", startTime); }
下面就去看packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java,bindHistory就是去获取曲线图要画的每个点的信息。
parse(mStats, parserList);
会去循环获取HistoryItem,它储存了每个时间点的电量,用来画图的时候用的。所以我们要去看HistoryItem是怎么获取的。
public void bindHistory(final UsageView view, BatteryDataParser... parsers) { final Context context = view.getContext(); BatteryDataParser parser = new BatteryDataParser() { SparseIntArray points = new SparseIntArray(); long startTime; int lastTime = -1; byte lastLevel; @Override public void onParsingStarted(long startTime, long endTime) { this.startTime = startTime; timePeriod = endTime - startTime; view.clearPaths(); // Initially configure the graph for history only. view.configureGraph((int) timePeriod, 100); } @Override public void onDataPoint(long time, HistoryItem record) { lastTime = (int) time; lastLevel = record.batteryLevel; points.put(lastTime, lastLevel); } @Override public void onDataGap() { if (points.size() > 1) { view.addPath(points); } points.clear(); } @Override public void onParsingDone() { onDataGap(); // Add projection if we have an estimate. if (remainingTimeUs != 0) { PowerUsageFeatureProvider provider = FeatureFactory.getFactory(context) .getPowerUsageFeatureProvider(context); if (!mCharging && provider.isEnhancedBatteryPredictionEnabled(context)) { points = provider.getEnhancedBatteryPredictionCurve(context, startTime); } else { // Linear extrapolation. if (lastTime >= 0) { points.put(lastTime, lastLevel); points.put((int) (timePeriod + PowerUtil.convertUsToMs(remainingTimeUs)), mCharging ? 100 : 0); } } } // If we have a projection, reconfigure the graph to show it. if (points != null && points.size() > 0) { int maxTime = points.keyAt(points.size() - 1); view.configureGraph(maxTime, 100); view.addProjectedPath(points); } } }; BatteryDataParser[] parserList = new BatteryDataParser[parsers.length + 1]; for (int i = 0; i < parsers.length; i++) { parserList[i] = parsers[i]; } parserList[parsers.length] = parser; parse(mStats, parserList); String timeString = context.getString(R.string.charge_length_format, Formatter.formatShortElapsedTime(context, timePeriod)); String remaining = ""; if (remainingTimeUs != 0) { remaining = context.getString(R.string.remaining_length_format, Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000)); } view.setBottomLabels(new CharSequence[] {timeString, remaining}); }
从代码上看HistoryItem是通过BatteryStats对象获取的,下面看BatteryStats对象怎么获取的。
final HistoryItem rec = new HistoryItem(); while (stats.getNextHistoryLocked(rec)) {
是通过pars(mStats,parserList)中传过去的,接着看mStats怎么获取的。它是通过getBatteryInfo获取的。
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast, BatteryStats stats, Estimate estimate, long elapsedRealtimeUs, boolean shortString) { final long startTime = System.currentTimeMillis(); BatteryInfo info = new BatteryInfo(); info.mStats = stats;
然后这个getBatteryInfo又是通过别的getBatteryInfo获取的,你会发现BatteryStats是通过BatteryStatsHelper获得的。而这个最上面的getBatteryInfo刚好是BatteryHistoryPreference中setStats的调用的,然后传了一个BatteryStatsHelper对象,所以我们要往回追。
public static void getBatteryInfo(final Context context, final Callback callback, final BatteryStatsHelper statsHelper, boolean shortString) { new AsyncTask() { @Override protected BatteryInfo doInBackground(Void... params) { return getBatteryInfo(context, statsHelper, shortString); } @Override protected void onPostExecute(BatteryInfo batteryInfo) { final long startTime = System.currentTimeMillis(); callback.onBatteryInfoLoaded(batteryInfo); BatteryUtils.logRuntime(LOG_TAG, "time for callback", startTime); } }.execute(); } public static BatteryInfo getBatteryInfo(final Context context, final BatteryStatsHelper statsHelper, boolean shortString) { final BatteryStats stats; final long batteryStatsTime = System.currentTimeMillis(); if (statsHelper == null) { final BatteryStatsHelper localStatsHelper = new BatteryStatsHelper(context, true); localStatsHelper.create((Bundle) null); stats = localStatsHelper.getStats(); } else { stats = statsHelper.getStats(); } BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime); final long startTime = System.currentTimeMillis(); PowerUsageFeatureProvider provider = FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context); final long elapsedRealtimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); final Intent batteryBroadcast = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); // 0 means we are discharging, anything else means charging final boolean discharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0; if (discharging && provider != null && provider.isEnhancedBatteryPredictionEnabled(context)) { Estimate estimate = provider.getEnhancedBatteryPrediction(context); if (estimate != null) { Estimate.storeCachedEstimate(context, estimate); BatteryUtils .logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime); return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats, estimate, elapsedRealtimeUs, shortString); } } final long prediction = discharging ? stats.computeBatteryTimeRemaining(elapsedRealtimeUs) : 0; final Estimate estimate = new Estimate( PowerUtil.convertUsToMs(prediction), false, /* isBasedOnUsage */ EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime); return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats, estimate, elapsedRealtimeUs, shortString); }
下面回去看BatteryHistoryPreference中的setStats中的BatteryStatsHelper怎么获取的,继续往回就回到了PowerUsageBase了。
protected void updatePreference(BatteryHistoryPreference historyPref) { final long startTime = System.currentTimeMillis(); historyPref.setStats(mStatsHelper); BatteryUtils.logRuntime(TAG, "updatePreference", startTime); }
然后接着看这个mStatsHelper怎么获取的。
@Override public void onAttach(Activity activity) { super.onAttach(activity); mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); mStatsHelper = new BatteryStatsHelper(activity, true); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mStatsHelper.create(icicle); setHasOptionsMenu(true); mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(getContext()); mBatteryBroadcastReceiver.setBatteryVoltageTempListener(mBatteryVoltageTempListener); mBatteryBroadcastReceiver.setBatteryChangedListener(type -> { restartBatteryStatsLoader(type); }); }public class PowerLoaderCallback implements LoaderManager.LoaderCallbacks { private int mRefreshType; @Override public Loader onCreateLoader(int id, Bundle args) { mRefreshType = args.getInt(KEY_REFRESH_TYPE); return new BatteryStatsHelperLoader(getContext()); } @Override public void onLoadFinished(Loader loader, BatteryStatsHelper statsHelper) { mStatsHelper = statsHelper; refreshUi(mRefreshType); } @Override public void onLoaderReset(Loader loader) { } }
后面我就追不下去了,没什么思路了。后来发现,既然这边追不下去,那么我是不是可以去看系统是怎么去设置HistroyItem的。后来不知道看到哪里了,因为我要找的是电量,所以直接去BatteryStatsImpl(BatteryStats是接口),中去看它怎么给HistoryItem赋值的。然后这边看看,那边看看。去里面搜索batterylevel,然后无意中发现了
setBatteryStateLocked
后来加了log,debug,就是这个去设置HistoryItem的电量。然后去frameworks/base下面去搜索看看,setBatteryStateLocked有哪些地方用了。发现services/core/java/com/android/server/am/BatteryStatsService.java中有使用,
@Override public void setBatteryState(final int status, final int health, final int plugType, final int level, final int temp, final int volt, final int chargeUAh, final int chargeFullUAh) { enforceCallingPermission(); // BatteryService calls us here and we may update external state. It would be wrong // to block such a low level service like BatteryService on external stats like WiFi. mWorker.scheduleRunnable(() -> { synchronized (mStats) { final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status); if (mStats.isOnBattery() == onBattery) { // The battery state has not changed, so we don't need to sync external // stats immediately. mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt, chargeUAh, chargeFullUAh); return; } } // Sync external stats first as the battery has changed states. If we don't sync // before changing the state, we may not collect the relevant data later. // Order here is guaranteed since we're scheduling from the same thread and we are // using a single threaded executor. mWorker.scheduleSync("battery-state", BatteryExternalStatsWorker.UPDATE_ALL); mWorker.scheduleRunnable(() -> { synchronized (mStats) { mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt, chargeUAh, chargeFullUAh); } }); }); }
下面在去看看setBatteryState哪里有用。 其实它使用了AIDL,public final class BatteryStatsService extends IBatteryStats.Stub
,然后搜索发现,services/core/java/com/android/server/BatteryService.java中有使用这个。
private void processValuesLocked(boolean force) { boolean logOutlier = false; long dischargeDuration = 0; mBatteryLevelCritical = mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN && mHealthInfo.batteryLevel <= mCriticalBatteryLevel; if (mHealthInfo.chargerAcOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_AC; } else if (mHealthInfo.chargerUsbOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_USB; } else if (mHealthInfo.chargerWirelessOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; } else { mPlugType = BATTERY_PLUGGED_NONE; } if (DEBUG) { Slog.d(TAG, "Processing new values: " + "info=" + mHealthInfo + ", mBatteryLevelCritical=" + mBatteryLevelCritical + ", mPlugType=" + mPlugType); } // Let the battery stats keep track of the current level. try { mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature, mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter, mHealthInfo.batteryFullCharge); } } catch (RemoteException e) { // Should never happen. }
最后其实就是BatteryService中,当电池变化了,就去设置一下。
setBatteryState里面的电量就是图上显示的,当然它会有一些处理,并且保存下来,形成一个列表。然后用的时候就可以取出来了,每个时间点的电量。
------------------------------------------------------------------------------------------------------------
PS:Last full charge 和 Screen usage since full charge也是通过这个触发计时的。
BatteryService setBatteryState() -> IBatteryStats setBatteryState() -> BatteryStatsService setBatteryState() -> BatteryStatsImpl setBatteryStateLock() -> setOnBatteryLocked()
BatteryStatsImpl。
从代码上可以看出,会更具电量等信息去处理,是否要重新计时。就是batteryservice setBatteryState那传过来的。
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) { 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 int screenState = mScreenState; 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 || (mDischargeCurrentLevel < 20 && level >= 80))) { 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.
更多相关文章
- 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Debian镜像
- Android获取屏幕大小
- 安卓(android)使用GPS,获取经纬度
- Android获取基带版本
- Android(安卓)获取存储卡路径和空间使用情况
- Android中获取屏幕相关信息(屏幕大小,状态栏、标题栏高度)
- android和j2me之清屏(clearScreen)
- Android电量和插拔电源状态广播监听
- android获取正在运行的进程