BatteryStatsService电池电量统计服务源码分析

BatteryStatsService主要负责电池电量的统计信息,首先我们简单的看下电量统计服务的启动过程。

BatteryStatsService启动过程

AA

从BatteryStatsService的启动时序图可以看出,BatteryStatsService服务是在ActivityManagerService服务中启动的

1. 在SystemServer中startBootstrapServices()方法中创建了ActivityManagerService服务的对象,并调用了SystemServiceManager的startService()方法启动了BatteryStatsService服务

首先分析BatteryStatsService的构造方法

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. mStats = new BatteryStatsImpl(systemDir, handler, mHandler);  
mStats = new BatteryStatsImpl(systemDir, handler, mHandler);

构造方法中创建了一个BatterystatsImpl对象,BatteryStatsService真正的处理逻辑其实都是在BatteryStatsImpl类中。

在BatteryStatsImpl类的构造函数中首先创建了一个文件

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. if (systemDir != null) {  
  2.             mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),  
  3.                     new File(systemDir, "batterystats.bin.tmp"));  
  4.         } else {  
  5.             mFile = null;  
  6.         }  
if (systemDir != null) {mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),new File(systemDir, "batterystats.bin.tmp"));} else {mFile = null;}

在手机的data/system/目录下创建一个batterystats.bin文件

该文件用于保存BatteryStats电量的统计信息,系统会不定时的将电量统计信息BatteryStats写入到文件中。

然后在构造函数中初始化了mScreenOnTimer,mPhoneOnTimer,

WifiOnTimer等一系列的StopWatchTimer,这些Timer主要用于统计各种模块耗电的时间。

2.在ActivityManagerService服务的构造方法中创建了BatteryStatsService服务的对象,调用了readLocked()方法读取电池统计信息,初始化BatteryStats

3.调用BatteryStatsService的initPwerManagerment()方法初始化电量统计服务,将BatteryStatsService服务注册到ServiceManager中

 

收集、存储电量统计信息

收集有很多的硬件模块,CPU、显示屏、WiFi、蓝牙、闪光灯等,在Android系统中,手机的电压是一定的,所以手机的电量计算就是模块使用时间*模块耗电。

其中关键就是模块使用时间的统计。电量信息的收集在内存中是有BatteryStats来描述的,由于收集的电量会从不同的维度来计算,所以该类设计的比较复杂。

电量收集的关系图。

BB

在BatterystatsService中有很多note*** 方法,这些方法就是 其他的模块调用用来统计相关的电量使用时间的。比如在PMS类中经常会有调用Notifier类相关的方法来进行通知,这些方法最终都会调用到BatteryStatsService中note***方法来通知该服务统计电量信息

通过其中一个时间收集的例子来学习下模块电量时间收集的过程。

比如使用闪光灯的时间收集过程。

 /frameworks/av/services/camera/libcameraservice/CameraService.cpp类中。

使用闪光灯的时候会调用notifier.noteFlashlightOn(cameraIdnewUid)

方法,当停止使用闪光灯的时候会调用notifier.noteFlashlightOff(cameraIdoldUid)方法。

首先我们看下noteFlashLigntOn方法,该方法最终调用BatteryStatsImpl类中的noteFlashLightOnLocked方法来处理

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public void noteFlashlightOnLocked(int uid) {  
  2.     //获取当前调用应用程序的Uid  
  3.         uid = mapUid(uid);  
  4.         //获取真实系统时间  
  5.         final long elapsedRealtime = SystemClock.elapsedRealtime();  
  6.         //获取系统运行时间  
  7.         final long uptime = SystemClock.uptimeMillis();  
  8.         if (mFlashlightOnNesting++ == 0) {  
  9. ……  
  10.             addHistoryRecordLocked(elapsedRealtime, uptime);  
  11.             //调用Timer开始计时  
  12.             mFlashlightOnTimer.startRunningLocked(elapsedRealtime);  
  13.         }  
  14.         //根据UiD来统计该应用中flash的使用时间  
  15.         getUidStatsLocked(uid).noteFlashlightTurnedOnLocked(elapsedRealtime);  
  16.     }  
public void noteFlashlightOnLocked(int uid) {//获取当前调用应用程序的Uiduid = mapUid(uid);//获取真实系统时间final long elapsedRealtime = SystemClock.elapsedRealtime();//获取系统运行时间final long uptime = SystemClock.uptimeMillis();if (mFlashlightOnNesting++ == 0) {
……addHistoryRecordLocked(elapsedRealtime, uptime);//调用Timer开始计时mFlashlightOnTimer.startRunningLocked(elapsedRealtime);}//根据UiD来统计该应用中flash的使用时间getUidStatsLocked(uid).noteFlashlightTurnedOnLocked(elapsedRealtime);}

该方法中会调用Timer开始统计时间,同时根据UID来统计该每个应用使用Flash的时间。这边有两中Timer,一种是总体的Timer,用来统计该模块总共的使用时长,另一种在每个应用相关的UID中也有Timer,用来统计每个应用使用该模块的时长

我们看下Timer的startRunningLocked方法,看看如何开始统计使用时间。


[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1.    void startRunningLocked(long elapsedRealtimeMs) {  
  2.           if (mNesting++ == 0) {  
  3.               //计算当前的batteryRealTime时间  
  4.               final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);  
  5. //根据当前时间记录更新时间mUpdateTime  
  6.               mUpdateTime = batteryRealtime;  
  7.               ……  
  8.               mCount++;  
  9.               mAcquireTime = mTotalTime;  
  10.                 
  11.           }  
  12.       }  
     void startRunningLocked(long elapsedRealtimeMs) {if (mNesting++ == 0) {//计算当前的batteryRealTime时间final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);//根据当前时间记录更新时间mUpdateTimemUpdateTime = batteryRealtime;……mCount++;mAcquireTime = mTotalTime;}}

该方法其实也是比较简单,在调用该方法的时候记录下更新时间mUpdateTime

noteFlashLightOff方法同样是调用Timer停止统计FlashLight的使用时间,同时按UID停止统计调用应用程序的使用时间。

Timer的stopRunningLocked方法

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. void stopRunningLocked(long elapsedRealtimeMs) {  
  2.             if (mNesting == 0) {  
  3.                 return;  
  4.             }  
  5.             if (--mNesting == 0) {  
  6.                 //计算现在的batteryRealTime时间  
  7.                 final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);  
  8.                 ……  
  9.                     mNesting = 1;  
  10.                     //计算该Timer统计的已经使用总时长totalTime  
  11.                     mTotalTime = computeRunTimeLocked(batteryRealtime);  
  12.                     mNesting = 0;  
  13.                 ……  
  14.             }  
  15.         }  
void stopRunningLocked(long elapsedRealtimeMs) {if (mNesting == 0) {return;}if (--mNesting == 0) {//计算现在的batteryRealTime时间final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);……mNesting = 1;//计算该Timer统计的已经使用总时长totalTimemTotalTime = computeRunTimeLocked(batteryRealtime);mNesting = 0;……}}

该方法中在停止统计的时候,根据batteryRealTime计算出该Timer统计的已经使用的总时长

结束当前的时间 – 开始的时间 = 模块使用时长

computeRunTimeLocked计算 总时长 = 总时长 + (结束当前的时间 – 开始的时间)

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. return mTotalTime + (batteryRealTime - mUpdateTime)  
return mTotalTime + (batteryRealTime - mUpdateTime)

到此为止可以看出,统计各个模块的使用时间实际就是 当该模块使用或者结束的时候,调用相应的方法开始统计和结束统计,该时间段的使用时间就是结束的时间 – 开始的时间。该模块的使用的总时间就是各个使用时间段的总和。

各个模块的使用时间统计最终保存在BatteryStats类中的各个timer中。

电量计算

计算耗电量离不开BatteryStatsHelper这个类,在Settings设置中显示电量相关信息就是调用BatteryStatsHelper的接口来实现的。

BatteryStatsHelper主要作用是用来计算所有应用和服务的用电量信息,使用BatteryStatsHelper接口的时候,必须在Activity或者Fragment初始化的时候调用BatteryStatsHelper的create()来初始化它,同时在activity或者Fragment销毁的时候调用destroy()方法来销毁它。 

 

首先我们看下BatteryStatsHelper的create()初始化方法。

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public void create(Bundle icicle) {  
  2.        mBatteryInfo = IBatteryStats.Stub.asInterface(  
  3.                ServiceManager.getService(BatteryStats.SERVICE_NAME));  
  4.        mPowerProfile = new PowerProfile(mContext);  
  5.    }  
 public void create(Bundle icicle) {mBatteryInfo = IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME));mPowerProfile = new PowerProfile(mContext);}

Create()方法中主要做了两件事:

1.获取了一个BatteryStatsService服务的对象。用于和BatteryStatsService进行通信。

2.创建了一个PowerProfile文件对象。我们知道Android系统中关于电量计算公式:耗电量 = 模块使用时间 * 单位时间内的耗电量

模块使用时间我们已经在BatteryStatsService中统计过了,那单位时间的耗电量其实就是在PowerProfile文件定义好了,每个手机的power_profile文件都不一样,需要厂商自己定义,只需要根据各个厂商定义的这个文件来计算相关的耗电量即可。

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public PowerProfile(Context context) {  
  2.        if (sPowerMap.size() == 0) {  
  3.            readPowerValuesFromXml(context);  
  4.        }  
  5.    }  
  6.   
  7.    private void readPowerValuesFromXml(Context context) {  
  8.        int id = com.android.internal.R.xml.power_profile;  
  9.        final Resources resources = context.getResources();  
  10.        XmlResourceParser parser = resources.getXml(id);  
  11.        boolean parsingArray = false;  
  12.        ArrayList<Double> array = new ArrayList<Double>();  
  13.        String arrayName = null;  
  14.   
  15.        try {  
  16.            XmlUtils.beginDocument(parser, TAG_DEVICE);  
  17.         ……  
 public PowerProfile(Context context) {if (sPowerMap.size() == 0) {readPowerValuesFromXml(context);}}private void readPowerValuesFromXml(Context context) {int id = com.android.internal.R.xml.power_profile;final Resources resources = context.getResources();XmlResourceParser parser = resources.getXml(id);boolean parsingArray = false;ArrayList<Double> array = new ArrayList<Double>();String arrayName = null;try {XmlUtils.beginDocument(parser, TAG_DEVICE);……

PowerProfile类初始化的时候,直接读取power_profile的xml文件,然后将预定义的值保存到MAP中。

Power_profile.xml文件

[html] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. <device name="Android">  
  2.   
  3.   <item name="none">0</item>  
  4.   <item name="screen.on">0.1</item>  <!-- ~200mA -->  
  5.   <item name="screen.full">0.1</item>  <!-- ~300mA -->  
  6.   <item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->  
  7.   <item name="bluetooth.on">0.1</item>  <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->  
  8.   <item name="wifi.on">0.1</item>  <!-- ~3mA -->  
  9.   <item name="wifi.active">0.1</item>  <!-- WIFI data transfer, ~200mA -->  
  10.   <item name="wifi.scan">0.1</item>  <!-- WIFI network scanning, ~100mA -->  
  11.   <item name="dsp.audio">0.1</item> <!-- ~10mA -->  
  12.   <item name="dsp.video">0.1</item> <!-- ~50mA -->  
  13.   ……  
<device name="Android"><item name="none">0</item><item name="screen.on">0.1</item>  <!-- ~200mA --><item name="screen.full">0.1</item>  <!-- ~300mA --><item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA --><item name="bluetooth.on">0.1</item>  <!-- Bluetooth on & connectable, but not connected, ~0.1mA --><item name="wifi.on">0.1</item>  <!-- ~3mA --><item name="wifi.active">0.1</item>  <!-- WIFI data transfer, ~200mA --><item name="wifi.scan">0.1</item>  <!-- WIFI network scanning, ~100mA --><item name="dsp.audio">0.1</item> <!-- ~10mA --><item name="dsp.video">0.1</item> <!-- ~50mA -->……

PowerProfile文件中定义了各个模块在不同状态下的耗电量,由于各个硬件模块的耗电量不相同,所以这个文件是由手机的硬件厂商来定制的。

 BatteryStatsHelper的refreshStats()方法用来刷新计算的用电量信息,是BatteryStatsHelper类的关键接口,电量计算也是在该方法在中进行处理的。

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,  
  2.            long rawUptimeUs) {  
  3.        //初始化基本的状态  
  4.        mMaxPower = 0;  
  5.        mMaxRealPower = 0;  
  6.        mComputedPower = 0;  
  7.        mTotalPower = 0;  
  8.        //重置相关的列表  
  9.        ……  
  10.        //初始化相关的电量计算工具,各种计算工具都继承自PowerCalculator类,用于计算不同模块的耗电量  
  11.        if (mCpuPowerCalculator == null) {  
  12.            mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);  
  13.        }  
  14.        mCpuPowerCalculator.reset();  
  15.   
  16.        ……  
  17.   
  18.        if (mFlashlightPowerCalculator == null) {  
  19.            mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);  
  20.        }  
  21.        mFlashlightPowerCalculator.reset();  
  22.   
  23.     //初始化相关的信息  
  24.        ……  
  25.     //获取电池剩余时间  
  26.        mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);  
  27.     //获取充电剩余时间  
  28.        mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);  
  29.        ……  
  30.     //计算各个应用的耗电量  
  31.        processAppUsage(asUsers);  
  32.   
  33. ……  
  34.   
  35.     //计算其他杂项的耗电量  
  36.        processMiscUsage();  
  37.   
  38.        Collections.sort(mUsageList);  
  39.   
  40.          
  41.     //计算耗电量最大的应用,并计算所有的应用的总耗电量  
  42.        if (!mUsageList.isEmpty()) {  
  43.            mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;  
  44.            final int usageListCount = mUsageList.size();  
  45.            for (int i = 0; i < usageListCount; i++) {  
  46.                mComputedPower += mUsageList.get(i).totalPowerMah;  
  47.            }  
  48.        }  
  49.   
  50.        mTotalPower = mComputedPower;  
  51.        //统计的总耗电量和实际总耗电量之间的差距  
  52.        if (mStats.getLowDischargeAmountSinceCharge() > 1) {  
  53.         //电池耗电量大于统计的总耗电量  
  54.            if (mMinDrainedPower > mComputedPower) {  
  55.                double amount = mMinDrainedPower - mComputedPower;  
  56.                mTotalPower = mMinDrainedPower;  
  57.                BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);  
  58.   
  59.                // Insert the BatterySipper in its sorted position.  
  60.                int index = Collections.binarySearch(mUsageList, bs);  
  61.                if (index < 0) {  
  62.                    index = -(index + 1);  
  63.                }  
  64.                mUsageList.add(index, bs);  
  65.                mMaxPower = Math.max(mMaxPower, amount);  
  66.         //电池耗电量小于统计的总耗电量  
  67.            } else if (mMaxDrainedPower < mComputedPower) {  
  68.                double amount = mComputedPower - mMaxDrainedPower;  
  69.   
  70.                // Insert the BatterySipper in its sorted position.  
  71.                BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);  
  72.                int index = Collections.binarySearch(mUsageList, bs);  
  73.                if (index < 0) {  
  74.                    index = -(index + 1);  
  75.                }  
  76.                mUsageList.add(index, bs);  
  77.                mMaxPower = Math.max(mMaxPower, amount);  
  78.            }  
  79.        }  
  80.    }  
 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,long rawUptimeUs) {//初始化基本的状态mMaxPower = 0;mMaxRealPower = 0;mComputedPower = 0;mTotalPower = 0;//重置相关的列表……//初始化相关的电量计算工具,各种计算工具都继承自PowerCalculator类,用于计算不同模块的耗电量if (mCpuPowerCalculator == null) {mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);}mCpuPowerCalculator.reset();……if (mFlashlightPowerCalculator == null) {mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);}mFlashlightPowerCalculator.reset();//初始化相关的信息……//获取电池剩余时间mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);//获取充电剩余时间mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);……//计算各个应用的耗电量processAppUsage(asUsers);……//计算其他杂项的耗电量processMiscUsage();Collections.sort(mUsageList);//计算耗电量最大的应用,并计算所有的应用的总耗电量if (!mUsageList.isEmpty()) {mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;final int usageListCount = mUsageList.size();for (int i = 0; i < usageListCount; i++) {mComputedPower += mUsageList.get(i).totalPowerMah;}}mTotalPower = mComputedPower;//统计的总耗电量和实际总耗电量之间的差距if (mStats.getLowDischargeAmountSinceCharge() > 1) {//电池耗电量大于统计的总耗电量if (mMinDrainedPower > mComputedPower) {double amount = mMinDrainedPower - mComputedPower;mTotalPower = mMinDrainedPower;BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);// Insert the BatterySipper in its sorted position.int index = Collections.binarySearch(mUsageList, bs);if (index < 0) {index = -(index + 1);}mUsageList.add(index, bs);mMaxPower = Math.max(mMaxPower, amount);//电池耗电量小于统计的总耗电量} else if (mMaxDrainedPower < mComputedPower) {double amount = mComputedPower - mMaxDrainedPower;// Insert the BatterySipper in its sorted position.BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);int index = Collections.binarySearch(mUsageList, bs);if (index < 0) {index = -(index + 1);}mUsageList.add(index, bs);mMaxPower = Math.max(mMaxPower, amount);}}}

这个方法主要逻辑就是来刷新电量统计,重新计算各个应用各个模块的电量。首先就重置了相关的基本变量,初始化了相关的电量计算工具。

最重要的是使用processAppUsage()方法来计算各个应用的耗电量。

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. private void processAppUsage(SparseArray<UserHandle> asUsers) {  
  2.         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);  
  3.         mStatsPeriod = mTypeBatteryRealtime;  
  4.   
  5.         BatterySipper osSipper = null;  
  6.         final SparseArray<? extends Uid> uidStats = mStats.getUidStats();  
  7.         final int NU = uidStats.size();  
  8.         //循环计算每个应用的耗电量  
  9.         for (int iu = 0; iu < NU; iu++) {  
  10.             final Uid u = uidStats.valueAt(iu);  
  11.             final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);  
  12.             //计算cpu耗电量  
  13.             mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  14.         //计算的wakeLock的耗电量  
  15.             mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  16.         //计算手机射频的耗电量  
  17.             mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  18.         //计算WiFI的耗电量  
  19.             mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  20.         //计算蓝牙的耗电量  
  21.             mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  22.         //计算传感器的耗电量  
  23.             mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  24.         //计算Camera的耗电量  
  25.             mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  26.         //计算闪光灯的耗电量  
  27.             mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);  
  28.   
  29.         //计算这个应用耗电量的总和  
  30.             final double totalPower = app.sumPower();  
  31.   
  32.             if (totalPower != 0 || u.getUid() == 0) {  
  33.                 //根据应用的类型,将应用耗电量信息添加到对应的列表  
  34.                 final int uid = app.getUid();  
  35.                 final int userId = UserHandle.getUserId(uid);  
  36.                 if (uid == Process.WIFI_UID) {  
  37.                     mWifiSippers.add(app);  
  38.                 } else if (uid == Process.BLUETOOTH_UID) {  
  39.                     mBluetoothSippers.add(app);  
  40.                 } else if (!forAllUsers && asUsers.get(userId) == null  
  41.                         && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {  
  42.                     //如果该应用是单独某个用户下的应用则添加的userSippers列表中  
  43.                     if (list == null) {  
  44.                         list = new ArrayList<>();  
  45.                         mUserSippers.put(userId, list);  
  46.                     }  
  47.                     list.add(app);  
  48.                 } else {  
  49.             //否则直接添加到普通的应用列表  
  50.                     mUsageList.add(app);  
  51.                 }  
  52.   
  53.                 if (uid == 0) {  
  54.           //如果应用id为0,则这个应用为android系统  
  55.                     osSipper = app;  
  56.                 }  
  57.             }  
  58.         }  
  59.   
  60.         if (osSipper != null) {  
  61.             mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime,  
  62.                                                         mRawUptime, mStatsType);  
  63.             osSipper.sumPower();  
  64.         }  
  65.     }  
private void processAppUsage(SparseArray<UserHandle> asUsers) {final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);mStatsPeriod = mTypeBatteryRealtime;BatterySipper osSipper = null;final SparseArray<? extends Uid> uidStats = mStats.getUidStats();final int NU = uidStats.size();//循环计算每个应用的耗电量for (int iu = 0; iu < NU; iu++) {final Uid u = uidStats.valueAt(iu);final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);//计算cpu耗电量mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算的wakeLock的耗电量mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算手机射频的耗电量mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算WiFI的耗电量mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算蓝牙的耗电量mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算传感器的耗电量mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算Camera的耗电量mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算闪光灯的耗电量mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算这个应用耗电量的总和final double totalPower = app.sumPower();if (totalPower != 0 || u.getUid() == 0) {//根据应用的类型,将应用耗电量信息添加到对应的列表final int uid = app.getUid();final int userId = UserHandle.getUserId(uid);if (uid == Process.WIFI_UID) {mWifiSippers.add(app);} else if (uid == Process.BLUETOOTH_UID) {mBluetoothSippers.add(app);} else if (!forAllUsers && asUsers.get(userId) == null&& UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {//如果该应用是单独某个用户下的应用则添加的userSippers列表中if (list == null) {list = new ArrayList<>();mUserSippers.put(userId, list);}list.add(app);} else {//否则直接添加到普通的应用列表mUsageList.add(app);}if (uid == 0) {//如果应用id为0,则这个应用为android系统osSipper = app;}}}if (osSipper != null) {mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime,mRawUptime, mStatsType);osSipper.sumPower();}}

processAppUsage()方法中利用循环的方法,一次计算每一个应用的耗电量。

1. 根据应用的uid来计算该应用的CPU,蓝牙,wifi,手机射频,传感器,camera等硬件的耗电量,所计算的耗电量信息都存储在了BatterySipper这个对象中。

2. 将各个硬件模块的耗电量相加计算出该应用的总耗电量

3. 根据应用的类型,将应用的耗电信息添加的不同的列表归类。其中若uid==0表示当前应用为系统,然后计算出系统的耗电量

那耗电量到底是怎么计算的呢?我们看其中一个电量计算工具的实现方法

WiFiPowerCalculator

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public WifiPowerCalculator(PowerProfile profile) {  
  2.         //获取WIFI三种模式下单位时间的耗电量  
  3.         mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);  
  4.         mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);  
  5.         mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);  
  6.     }  
  7.   
  8.     @Override  
  9.     public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,  
  10.                              long rawUptimeUs, int statsType) {  
  11.         //获取WiFi三种模式,每种模式的使用时长  
  12.         final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,  
  13.                 statsType);  
  14.         final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);  
  15.         final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);  
  16.         //该应用的WiFi使用总时长  
  17.         app.wifiRunningTimeMs = idleTime + rxTime + txTime;  
  18.         //计算该应用WIFi耗电总量  
  19.         app.wifiPowerMah =  
  20.                 ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))  
  21.                 / (1000*60*60);  
  22.         //累加到总耗电量中  
  23.         mTotalAppPowerDrain += app.wifiPowerMah;  
  24.         //计算各个模式下传输的数据包  
  25.         app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,  
  26.                 statsType);  
  27.         app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,  
  28.                 statsType);  
  29.         app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,  
  30.                 statsType);  
  31.         app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,  
  32.                 statsType);  
  33.   
  34.     }  
public WifiPowerCalculator(PowerProfile profile) {//获取WIFI三种模式下单位时间的耗电量mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);}@Overridepublic void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,long rawUptimeUs, int statsType) {//获取WiFi三种模式,每种模式的使用时长final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,statsType);final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);//该应用的WiFi使用总时长app.wifiRunningTimeMs = idleTime + rxTime + txTime;//计算该应用WIFi耗电总量app.wifiPowerMah =((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))/ (1000*60*60);//累加到总耗电量中mTotalAppPowerDrain += app.wifiPowerMah;//计算各个模式下传输的数据包app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,statsType);app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,statsType);app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,statsType);app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,statsType);}

可以看出在构造方法中,首先从PowerProfile中来获取预定义的WiFi单位时间的耗电量,在计算app耗电量calculateApp()方法中,获取该应用WiFi三种模式的使用时长,将三种模式的使用时长相加得出该应用使用WiFI的总时长,耗电量就是 app.wifiPowerMah =  ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))/ (1000*60*60);

WiFi相应模式的使用时长 * 相应模式的单位时间耗电量。

最后还计算不同模式下的传输的数据包。

从代码实现中可以看出电量计算公式: 耗电量 = 耗电时长 * 单位时间耗电量

其他模块的耗电量和WiFi耗电量计算类似,不再一一分析。

接着看refreshStats()方法。

当计算完各个应用的耗电量后调用processMiscUsage()方法,根据方法名称就可以看出该方法主要用来计算各种杂项的耗电量

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. private void processMiscUsage() {  
  2.        addUserUsage();  
  3.        addPhoneUsage();  
  4.        addScreenUsage();  
  5.        addWiFiUsage();  
  6.        addBluetoothUsage();  
  7.        addIdleUsage(); // Not including cellular idle power  
  8.        // 如果手机只是支持wifi,就不在计算射频的电量  
  9.        if (!mWifiOnly) {  
  10.            addRadioUsage();  
  11.        }  
  12.    }  
 private void processMiscUsage() {addUserUsage();addPhoneUsage();addScreenUsage();addWiFiUsage();addBluetoothUsage();addIdleUsage(); // Not including cellular idle power// 如果手机只是支持wifi,就不在计算射频的电量if (!mWifiOnly) {addRadioUsage();}}

该方法中主要用来计算那些WiFi, Phohe,Bluetooth等用电量,并将该用电信息添加到用电列表,以WiFi为例,计算WiFi的耗电量:WIFI模块的整体耗电量减去App中WiFI的耗电量

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. private void addWiFiUsage() {  
  2.         BatterySipper bs = new BatterySipper(DrainType.WIFI, null0);  
  3.         计算WiFi的耗电量  
  4.         mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);  
  5.         将之前WiFi类型的应用电量相加  
  6.         aggregateSippers(bs, mWifiSippers, "WIFI");  
  7.         if (bs.totalPowerMah > 0) {  
  8.             mUsageList.add(bs);  
  9.         }  
  10.     }  
private void addWiFiUsage() {BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);计算WiFi的耗电量mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);将之前WiFi类型的应用电量相加aggregateSippers(bs, mWifiSippers, "WIFI");if (bs.totalPowerMah > 0) {mUsageList.add(bs);}}

在计算应用的耗电量的时候,我们会为每个应用计算相应模块WiFI的使用时长,同时我们也会统计WiFi模块的整体使用时长,两者之间的耗电量会有差距,我们将这部分差值就是WiFi耗电量

[java] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,  
  2.                                    long rawUptimeUs, int statsType) {  
  3.     //获取WiFi各种模式下使用总时长  
  4.     final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,  
  5.                 statsType);  
  6.     final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME,  
  7.                 statsType);  
  8.     final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME,  
  9.                 statsType);  
  10.     app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;  
  11.   
  12.     //获取WiFi模块消耗的总电量  
  13.     double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,  
  14.                 statsType) / (double)(1000*60*60);  
  15.     if (powerDrainMah == 0) {  
  16.        //如果WiFi模块消耗的总电量=0;重新计算WiFI消耗总电量.  
  17.        powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)  
  18.                     + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);  
  19.         }  
  20.         //求出WiFI消耗总电量 – 各个应用wifi消耗总电量  
  21.         app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);  
  22.     }  
public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,long rawUptimeUs, int statsType) {//获取WiFi各种模式下使用总时长final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,statsType);final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME,statsType);final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME,statsType);app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;//获取WiFi模块消耗的总电量double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,statsType) / (double)(1000*60*60);if (powerDrainMah == 0) {//如果WiFi模块消耗的总电量=0;重新计算WiFI消耗总电量.powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)+ (rxTimeMs * mRxCurrentMa)) / (1000*60*60);}//求出WiFI消耗总电量 – 各个应用wifi消耗总电量app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);}

这个方法中,求出了应用中WiFI模块使用总电量和Wifi消耗总电量之间的差值,属于没有应用认领的消耗的电量。

然后调用aggregateSippers()方法将我们以前统计的WIFI类型的应用耗电量相加,计算WiFi的耗电量。添加到usageList列表中。

其他屏幕,蓝牙耗电量类似。

 

然后再次回到refreshStats()方法中

调用Collection.sort()将电量使用排序,找到消耗电量最多的应用

将usageList列表中各个应用的耗电量相加得出统计的总耗电量,最后根据统计的总耗电量和实际的总耗电量之间的差距,作为未统计的类型,加入到耗电量的列表中

到此为止我们对于手机电量的统计与计算就分析完了

总结:

电量统计:实际使用StopWatchTImer来统计不同模块的使用时长,调用note**startLocked和note**stopLocked方法来开始和停止统计。

电量计算:根据统计的各个模块的使用时长 * 各个模块的单位时间的耗电量得出电量消耗。




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24244.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 4.4 batteryservice 电池电量显示分析

转载地址&#xff1a;http://blog.csdn.net/daweibalang717/article/details/40615453 最近工作接触到这么的东西&#xff0c;这是我对整个电池管理方面Java 层的分析。如果想了解底层的话&#xff0c;请看我的博客&#xff1a; android 4.4 电池电量管理底层分析(C\C层) &a…

Battery Historian分析手机耗电神器

极力推荐Android 开发大总结文章&#xff1a;欢迎收藏程序员Android 力荐 &#xff0c;Android 开发者需要的必备技能 本篇文章主要介绍 Android 开发中 电量 的部分知识点&#xff0c;通过阅读本篇文章&#xff0c;您将收获以下内容: 1.安装Battery Historian 2.收集Batteryst…

卡尔曼滤波预测应用python实践

1. 什么是卡尔曼滤波 最佳线性滤波理论起源于二十世纪40年代美国科学家Wiener和前苏联科学家KOnMoropOB等人的研究工作&#xff0c;后人统称为维纳滤波理论。60年代Kalman把状态空间模型引入滤波理论&#xff0c;并导出了一套递推估计算法&#xff0c;后人称之为卡尔曼滤波理论…

BatteryStatsService电池电量统计服务分析

BatteryStatsService主要负责电池电量的统计信息,首先我们简单的看下电量统计服务的启动过程。 BatteryStatsService启动过程 从BatteryStatsService的启动时序图可以看出,BatteryStatsService服务是在ActivityManagerService服务中启动的 1. 在SystemServer中startBootstra…

使用Battery Historian采集android耗电数据

一、简介 Battery Historian是由Google提供的查看电量相关信息及事件的工具&#xff0c;Battery historian还可以上传bugreport文件&#xff0c;分析用户手机中App的电池耗电情况。详见&#xff1a;GitHub - google/battery-historian: Battery Historian is a tool to analyz…

利用XGBoost实现短期电力负荷预测

💥 项目专栏:【机器学习项目实战案例目录】项目详解 + 完整源码 文章目录 一、利用XGBoost实现短期电力负荷预测二、数据集介绍三、将数据进行标准化四、形成训练数据五、划分训练集、测试集六、定义模型七、模型训练八、训练集、测试集验证九、网络搜索十、绘制结果🌠 『…

性能优化十四之电量分析工具Battery Historian使用

上篇博客中并没有讲解BatterHistory该如何使用&#xff0c;这篇博客就开始给大家进行介绍。 数据准备 battery-historian工具需要使用bugreport中的BatteryHistory 1. 先断开adb服务&#xff0c;然后开启adb服务 adb kill-server 这一步很重要&#xff0c;因为当我们开…

使用BatteryHistorian分析和优化应用电量

欢迎Follow我的GitHub, 关注我的CSDN. 本文的合集已经编著成书&#xff0c;高级Android开发强化实战&#xff0c;欢迎各位读友的建议和指导。在京东即可购买&#xff1a;https://item.jd.com/12385680.html 在Android项目中, 较难监控应用的电量消耗, 但是用户却非常关心手机的…

Android 使用 Batterystats 和 Battery Historian 分析电池用量

Android 使用 Batterystats 和 Battery Historian 分析电池用量 专注于Android开发&#xff0c;分享经验总结&#xff0c;欢迎加入 Android开发中涉及到耗电量怎么分析呢 google官方提供了文档&#xff0c;链接&#xff1a;https://developer.android.com/studio/profile/batt…

GT的使用及耗电量统计的原理,对GT采集的电池数据采用VBA分析

注意&#xff1a;我测试时的ios操作系统是8.1&#xff0c;现在ios9.0及以后的耗电量比老的系统少了一个量级&#xff0c;官方说减少20%&#xff0c;我的手机在熄屏的耗电量减少了30%以上&#xff0c;亮屏幕减少不太多。 具体编译宏文件和GT采集的部分数据文件下载地址是&#…

Android电池功耗BatteryHistorian数据分析

BatteryHistorian的用法 BatteryHistorian环境搭建配置 官方文档 1.BatteryHistorian图形页面数据分析: Timeline: System stats: CPU runing: cpu运行的状态 Kernel only uptime: 只有kernell运行 Userspace wakelock: 用户空间申请的锁 Screen: 屏幕是否点亮 Top …

时间序列预测:用电量预测 07 灰色预测算法

&#x1f32e;开发平台&#xff1a;jupyter lab &#x1f356;运行环境&#xff1a;python3、TensorFlow2.x ----------------------------------------------- 2022.9.16 测验成功 ---------------------------------------------------------------- 1. 时间序列预测&#x…

时间序列预测15:Multi-input / Multi-head CNN 实现用电量/发电量预测

【时间序列预测/分类】 全系列60篇由浅入深的博文汇总&#xff1a;传送门 接上文&#xff0c;本文介绍如何为多变量数据开发多输入通道多步时间序列预测的CNN模型和多子模型异构多步时间序列预测的CNN模型。 文章目录 2. 多输入通道 CNN 模型2.1 建模2.2 完整代码 3. 多头&…

人工智能迅猛发展,如何应对避免失业?

“人工智能从感知智能向认知智能演进”&#xff0c;人工智能的发展带给我们哪些思考&#xff1f;它究竟会给我们的工作生活带来哪些变化?我们要如何去应对&#xff1f; 阿里达摩院票选出2020年十大科技趋势&#xff1a; 量子计算进入攻坚期工业互联网的超融合保护数据隐私的A…

【杂谈】人脸图像书看完了感觉不过瘾?这些拓展人脸资料值得你关注一下

相信许多朋友都看过我这本人脸图像处理的书籍了&#xff0c;内容涵盖了人脸检测&#xff0c;人脸关键点检测&#xff0c;人脸识别&#xff0c;人脸属性分析&#xff0c;人脸美颜&#xff0c;人脸编辑与风格化&#xff0c;三维人脸重建内容&#xff0c;基本上包括了人脸的所有领…

Guava、Spring 如何抽象观察者模式?

什么是观察者模式 观察者模式 是一种行为设计模式&#xff0c;允许定义一种订阅通知机制&#xff0c;可以在对象&#xff08;被观察者&#xff09;事件发生时通知多个 “观察” 该对象的观察者对象&#xff0c;所以也被称为 发布订阅模式 其实我个人而言&#xff0c;不太喜欢使…

苹果「Find My iPhone」立功,帮警察追踪偷车嫌犯

By 超神经 内容提要&#xff1a;在澳大利亚墨尔本的一起入室盗窃案中&#xff0c;警方在 iPad 上「Find My」的协助下追踪到嫌犯位置&#xff0c;但追踪过程中嫌犯却因车祸丧生。 关键词&#xff1a;Find My 协警 盗窃 苹果的「Fing My」最近在一起入室抢劫案中立功了。 2 月 …

Hive 知识体系保姆级教程

Hive涉及的知识点如下图所示&#xff0c;本文将逐一讲解&#xff1a; 正文开始&#xff1a; 一. Hive概览 1.1 hive的简介 Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张数据库表&#xff0c;并提供类SQL查询功能。 其本质是将SQL转换为Map…

跟着 Guava、Spring 学习如何设计观察者模式

文章首发在公众号&#xff08;龙台的技术笔记&#xff09;&#xff0c;之后同步到掘金和个人网站&#xff1a;xiaomage.info 今天讲解一篇行为型设计模式&#xff0c;什么是行为型&#xff1f;行为型主要负责设计 类或对象之间的交互。工作中常用的观察者模式就是一种行为型设…

【总结】有三AI重要原创人脸相关的技术文章汇总(2022年8月)

人脸图像的应用领域想必所有做视觉算法的都不会陌生&#xff0c;所有的安防监控、几乎现在所有的在线身份认证、支付、考勤都需要用到人脸检测与人脸识别&#xff0c;人脸识别甚至被用于追逃疑犯&#xff0c;找寻失踪人口。 所有的在线直播平台&#xff0c;手机拍照软件&#x…