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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java中switch的用法和逻辑运算符[通俗易懂]

    java中switch的用法和逻辑运算符[通俗易懂]一、switch的用法常见用法如下:importjava.util.Scanner;publicclassA{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intscore=sc.nextInt();if(score>100&&score<0){Syst

    2022年7月7日
    20
  • SOA到底是什么

    SOA到底是什么SOA到底是什么 首先,SOA是一种开发思想。是一种松耦合的框架。可以让软件超越开发语言。SOA强调的是一种架构思想,组件化的灵活的开发方式。SOA是一场革命。一个应用程序的业务逻辑(businesslogic)或某些单独的功能被模块化并作为服务呈现给消费者或客户端。这些服务的关键是他们的松耦合特性。例如,服务的接口和实现相独立。应用开发人员或者系统集成者可以通过组合一个或多个服务来…

    2022年6月17日
    28
  • JAVA初体验

    JAVA初体验目录 nbsp 1 什么是软件开发 2 人机交互方式 3 JAVA 语言概述 3 1JAVA 语言的三种技术架构 3 2JAVA 语言的跨平台性 4 JAVA 开发环境 4 1 nbsp JDK JRE JVM 之间的关系 4 2 为什么配置 path 变量 5 Java 程序的编写 编译和运行 1 什么是软件开发 软件 一系列按照特定顺序组织的计算机数据和指令的集合 软件是由程序 数

    2025年10月25日
    3
  • Activiti工作流的详细介绍[通俗易懂]

    Activiti工作流的详细介绍[通俗易懂]一、了解工作流1、工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现。2、工作流管理系统(WorkflowManagementSystem,WfMS)是一个软件系统,它完成工作量的定义和管理,并按照在系统中预先定义好的工作流逻辑进行工作流实例的执行。工作流管理系统不是企业的业务系统,而是为企业的业务系统的运行提供了一个软件的

    2022年10月6日
    2
  • 光流法运动目标检测[通俗易懂]

    光流法运动目标检测[通俗易懂]接上篇,OpenCV视频目标跟踪及背景分割器,本篇介绍OpenCV—python目标跟踪==》光流法回顾:目标跟踪是对摄像头视频中的移动目标进行定位的过程。实时目标跟踪是许多计算机视觉应用的重要任务,如监控、基于感知的用户界面、增强现实、基于对象的视频压缩以及辅助驾驶等。关于实现视频目标跟踪的方法有很多,当跟踪所有移动目标时,帧之间的差异会变的有用;当跟踪视频中移动的手时,基于皮肤颜色的均…

    2022年7月23日
    18
  • 稻城亚丁4天自由行攻略_稻城亚丁3天旅行攻略

    稻城亚丁4天自由行攻略_稻城亚丁3天旅行攻略有人说向往什么,就去追求什么,才能求仁得仁。稻城亚丁,人称“最后的香格里拉”,藏语也称香巴拉,意为“心中的日月”,外人视为世外桃源,是作为川西秘境中最值得我们去探访的圣地。如果向往真爱和自由,那就带上

    2022年8月1日
    7

发表回复

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

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