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.

 

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • vim 配置文件 .vimrc

    vim 配置文件 .vimrc在终端下使用vim进行编辑时,默认情况下,编辑的界面上是没有显示行号、语法高亮度显示、智能缩进等功能的。为了更好的在vim下进行工作,需要手动设置一个配置文件:.vimrc。在启动vim时,当前用户根目录下的.vimrc文件会被自动读取,该文件可以包含一些设置甚至脚本,所以,一般情况下把.vimrc文件创建在当前用户的根目录下比较方便,即创建的命令为:$vi~/.vi

  • vscode安装java运行环境_vscode eclipse对比

    vscode安装java运行环境_vscode eclipse对比vscode中怎么搭建Java开发环境?下面本篇文章给大家介绍一下VSCode配置Java开发环境的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。配置Java开发环境主要参考官方教程:https://code.visualstudio.com/docs/java/java-tutorial1.先安装JDKJDK下载地址:https://www.oracle.com/java…

  • linux卸载已安装软件的命令_软件卸载不了

    linux卸载已安装软件的命令_软件卸载不了1、删除软件方法一、如果你知道要删除软件的具体名称,可以使用12方法二、如果不知道要删除软件的具体名称,可以使用1sudoapt-getpurge一个带core的package,如果

  • 安卓中activity的生命周期_activity生命周期调用顺序

    安卓中activity的生命周期_activity生命周期调用顺序前言很高兴遇见你~欢迎阅读我的文章。关于Activity生命周期的文章,网络上真的很多,有很多的博客也都讲得相当不错,可见Activity的重要性是非常高的。事实上,我猜测每个android开发者接触的第一个android组件都是Activity。我们从新建第一个Activity开始,运行了代码,看到模拟机上显示了一个MainActivity标题和一行HolleWorld,从此打开Android世界的大门。本篇文章讲解的重点是Activity的生命周期,在文章的最后也会涉及Activity的设计。不

  • Matlab中的画图函数

    Matlab中的画图函数之前在进行Matlab编程时,画图总是非常重要的一部分,在这里整理一下常用的绘图函数,以作备用。

  • 2、Java基础02 – 【命令行运行HelloWorld】[通俗易懂]

    2、Java基础02 – 【命令行运行HelloWorld】[通俗易懂]操作步骤:1、新建一个文件夹(可以命名为Java)2、新建一个.txt文本文件,在文件中输入如下代码:publicclassHelloWorld{publicstaticvoidmain(String[]args){System.out.println(“helloworld”);}}3、重命名将文件名改为HelloWorld.java,并创建第一个java源文件4、编译.java文件是java的源文件,但是不能直接运行,必须先被编译成为.class文件才能够

发表回复

您的电子邮箱地址不会被公开。

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