Android Q 电量使用图分析 show app usage「建议收藏」

Android Q 电量使用图分析 show app usage「建议收藏」这个界面就是packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageAdcanced.java,然后查看它加载的布局文件@OverrideprotectedintgetPreferenceScreenResId(){returnR.xml.power_usage_advanced;}布局代码如下<PreferenceScreenxmlns…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

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;
    }

Jetbrains全家桶1年46,售后保障稳定

布局代码如下 

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="power_usage_advanced_screen"
    android:title="@string/advanced_battery_title"
    settings:keywords="@string/keywords_battery">

    <com.android.settings.fuelgauge.BatteryHistoryPreference
        android:key="battery_graph"/>

    <PreferenceCategory
        android:key="app_list"
        android:title="@string/power_usage_list_summary"/>

</PreferenceScreen>

从布局上看,我们可以发现,我们要找的那个曲线图就是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<Void, Void, BatteryInfo>() {
            @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<BatteryStatsHelper> {
        private int mRefreshType;

        @Override
        public Loader<BatteryStatsHelper> onCreateLoader(int id,
                Bundle args) {
            mRefreshType = args.getInt(KEY_REFRESH_TYPE);
            return new BatteryStatsHelperLoader(getContext());
        }

        @Override
        public void onLoadFinished(Loader<BatteryStatsHelper> loader,
                BatteryStatsHelper statsHelper) {
            mStatsHelper = statsHelper;
            refreshUi(mRefreshType);
        }

        @Override
        public void onLoaderReset(Loader<BatteryStatsHelper> 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.

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/219176.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • mysql查看版本sql_linux查看mysql版本

    mysql查看版本sql_linux查看mysql版本查询mysql版本(select查mysql版本)2020-07-2411:32:47共10个回答1、通过mysql的-V参数查询版本信息mysql-V2、登录mysql时可以查询版本信息mysql-u用户名-p用户密码3、数据库状态查询版本信息status;4、通过mysql数据库函数查询版本信息selectversion();5、通过version环境变量查询版本信息执行selectvers…

    2022年10月7日
    0
  • pycharm环境变量配置失败_pycharm配置anaconda虚拟环境

    pycharm环境变量配置失败_pycharm配置anaconda虚拟环境去年编程的时候其实就看到有一些提示信息说,2021年元旦之后,python3.5就不再支持了。没想到今天配置新电脑,下载了新的pycharm版本的时候,就遇到这个问题如果一个环境前面显示了unsupported那表明,这个版本太老了,不再被支持。参考Pycharm社区文档:ConfigureaPythoninterpreter当pycharm不再支持一个过期的python版本时,对应的python编译器会被标记为unsupported…

    2022年8月28日
    2
  • 揭秘vista引导机制

    揭秘vista引导机制揭秘vista引导机制   所谓的引导机制就是在操作系统内核运行前的一小段程序。其主要作用是初始化电脑硬件设备,建立内存空间的映射图。从而将系统的软件和硬件设备环境调配到一个适合的状态,以使电脑最终调用系统内核而准备好适合的环境。   那么vista的引导机制是否和以前的windows的版本不同呢?其实vista引导机制是一项全新的技术。以前寄予nt的windows系统采用“ntl

    2022年10月10日
    0
  • 关于Pycharm Interpreter报错 Invalid Python SDK/Permission Denied问题解决方案「建议收藏」

    关于Pycharm Interpreter报错 Invalid Python SDK/Permission Denied问题解决方案「建议收藏」1.问题背景之前用了Pycharm好好的,结果运行个简单的test.py文件,里面的内容是:print(“helloworld”)居然报错,程序运行居然不是exitcode0报错结果如下:程序返回了一个异常参数,这个参数实际上是说:“你的python解释器未安装成功”。Processfinishedwithexitcode-1073741515(0xC0000135)网上百度了这个参数报错的意思,我就去找了我的解释器:【File】–>【Setting】,找到解释

    2022年8月26日
    13
  • Java中数组的声明格式

    Java中数组的声明格式Java中数组的声明格式1.类型标识符数组名[];(合法但不推荐)2.类型标识符[]数组名;(《java开发手册》强制)举例:int[]arr;String[]example;MyClass[]mc;//此时仅声明了引用变量,并未产生数组对象​注意声明时不可在方括号内指定数组大小​如float[10]arr;是错误的…

    2022年5月25日
    40
  • webservice传递特殊字符时的解决的方法

    webservice传递特殊字符时的解决的方法

    2021年11月29日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号