Android电量统计
前言
在维护电量管家应用以及学习处理一些功耗问题的时候,经常会接触电量统计相关的知识,抽空总结下这块知识,方便自己以及他人的学习。
电量统计
概述
在Andorid系统中的电量统计分为两种:一种是对于软件的耗电统计;一种是对于硬件设备的耗电统计。很多产商会根据系统对于这两种耗电类型的统计,给用户展示用电详情。
而在这个过程中主要的参与者如下:
/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl
/framework/base/core/java/com/andorid/internal/os/PowerProfile.java
/framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java
/framework/base/core/java/com/andorid/internal/os/BatterySipper.java
/framework/base/core/res/res/xml/power_profile.xml
其中:
BatteryStatsImpl是用以收集电量使用信息的实现类,继承自BatteryStats,在电量统计日志分析这篇文章中所描述的电量统计日志相关的东西,都是从这个类中收集交于BatteryStatsService来dump出来的,如下:
//BatteryStatsService
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
...
synchronized (mStats) {
mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
if (writeData) {
mStats.writeAsyncLocked();
}
}
}
//BatteryStatsImpl
public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
...
if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
pw.println("Statistics since last charge:");
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
//调用重载方法,输出电量统计信息
dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid,
(flags&DUMP_DEVICE_WIFI_ONLY) != 0);
pw.println();
}
}
PowerProfile.java用于获取各个组件的电流数值;power_profile.xml是一个可配置的功耗数据文件,每个手机产商都有自己私有的power_prfile.xml。
BatteryStatsHelper则是用来统计各个应用,多用户的每个用户,以及蓝牙,屏幕等耗电统计的类,其中processAppUsage()方法是用以计算应用软件耗电详情,processMiscUsage()方法是用来计算硬件耗电详情。
BatterySipper是耗电信息的实体类,主要包括应用耗电,系统服务耗电,硬件耗电等类型。
电量统计服务
电量统计服务BatteryStatsService(BSS),顾名思义就是用来统计电量使用情况的服务,下面对其做简单的介绍。
接触过系统源码的人应该多少都有了解,系统在启动后,会在SystemServer中启动各种服务,典型的如AMS,PMS,WMS等,当然电量统计服务BSS也会在这个时机中被创建出来,值得注意的是BSS真正创建的时机是在AMS被创建时,其依附于AMS,下面我们来看看源码。
public ActivityManagerService(Context systemContext) {
...
//创建电量状态服务
mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
mBatteryStatsService.getActiveStatistics().readLocked();
mBatteryStatsService.scheduleWriteToDisk();
mOnBattery = DEBUG_POWER ? true
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
...
}
private void start() {
Process.removeAllProcessGroups();
//启动CPU调度线程
mProcessCpuThread.start();
//设置服务的参数,并添加到ServiceManager
mBatteryStatsService.publish(mContext);
mAppOpsService.publish(mContext);
Slog.d("AppOps", "AppOpsService published");
//LocalServices类似于ServiceManager的功能,主要用于系统进程内部访问的一些服务
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
}
从上面可以看出,BSS是在AMS的构造方法中被创建出来的,随后在AMS的start方法中设置其服务的参数,我们先回过头看下BSS的构造方法里,做了些什么。
BatteryStatsService(File systemDir, Handler handler) {
// Our handler here will be accessing the disk, use a different thread than
// what the ActivityManagerService gave us (no I/O on that one!).
//创建电量状态服务消息队列
final ServiceThread thread = new ServiceThread("batterystats-sync",
Process.THREAD_PRIORITY_DEFAULT, true);
thread.start();
//消息处理对象
mHandler = new BatteryStatsHandler(thread.getLooper());
// BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
//对电量状态服务的具体实现类
mStats = new BatteryStatsImpl(systemDir, handler, mHandler, this);
}
可以看到,在BSS的构造方法中,先是创建了电量统计服务的消息队列用来接收消息,然后创建了消息处理对象用来处理消息,最后创建了电量统计收集类BatteryStatsImpl,从而为电量统计服务做好准备,这里也可以看出BSS与BatteryStatsImpl之间的关系。
我们在回头看下,AMS中的start方法调用的BSS中的publish方法。
public void publish(Context context) {
mContext = context;
mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout)
* 1000L);
//设置电量统计服务的基础信息 单位时间内的电量统计,配置文件位于frameworks/base/core/res/res/xml/power_profile.xml
mStats.setPowerProfile(new PowerProfile(context));
ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
}
public PowerProfile(Context context) {
// Read the XML file for the given profile (normally only one per
// device)
if (sPowerMap.size() == 0) {
readPowerValuesFromXml(context);
}
initCpuClusters();
}
private void readPowerValuesFromXml(Context context) {
int id = com.android.internal.R.xml.power_profile;
...
}
可以到看publish方法主要是设置了一些配置信息,而setPowerProfile方法就是设置了系统的功耗数据文件,在创建PowerProfile对象的同时会去读取系统的功耗配置文件信息,从而获取组件信息以及各个组件的电流数值。前面说到BSS中真正去收集信息的是BatteryStatsImpl,而BatteryStatsImpl中其父类方法中创建了BatteryStatsHelper对象,BatteryStatsHelper的构造方法也会去读取该配置信息,并且根据这些数值,对耗电信息进行计算统计。
//BatteryStatsImpl extends BatteryStats
final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
helper.create(this);
helper.refreshStats(which, UserHandle.USER_ALL);
final List<BatterySipper> sippers = helper.getUsageList();
至此BSS就被创建起来了,为系统提供电量统计服务。
注:上述只是简单的介绍了下BSS,读者有兴趣,可以自行看源码,深入理解。
了解了电量统计服务,下面来学习下耗电统计的计算方法。
耗电计算方法
前面说过,BatteryStatsHelper负责计算统计软件与硬件耗电详情。再聊计算方法之前,先说下这个类中的几个方法。
首先是create()方法,该方法有重载,用来获取用电信息,功耗配置信息等。在计算获取耗电详情时,必须先调用该方法。
public void create(BatteryStats stats) {
mPowerProfile = new PowerProfile(mContext);
mStats = stats;
}
public void create(Bundle icicle) {
if (icicle != null) {
mStats = sStatsXfer;
mBatteryBroadcast = sBatteryBroadcastXfer;
}
mBatteryInfo = IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME));
mPowerProfile = new PowerProfile(mContext);
}
其次是refreshStats()顾名思义就是用来刷新状态的方法,该方法有多个重载方法,最终都会调用以下方法。
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
long rawUptimeUs) {
//内容太多,就不附上了
...
processAppUsage(asUsers);
...
processMiscUsage();
...
}
refreshStats方法中会刷新很多数据,对于计算方法中,最主要是调用了这两个函数,一个是processAppUsage()方法用以计算软件耗电,一个是processMiscUsage()用以计算硬件耗电。
下面我们分别来介绍这两种类型的计算方法。
软件类
软件耗电是针对单个用户而言的,大家都知道Android是支持多用户的,所以软件耗电需要传入用户信息。
private void processAppUsage(SparseArray<UserHandle> asUsers) {
//判断是否统计所有用户的App耗电使用情况,目前该参数为true
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
mStatsPeriod = mTypeBatteryRealtimeUs;//耗电统计时长
BatterySipper osSipper = null;
//每个uid的统计信息
final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
//遍历每个uid的耗电情况
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, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//锁唤醒功耗
mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//无线电功耗
mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//WIFI功耗
mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//蓝牙功耗
mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//传感器功耗
mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//相机功耗
mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
//闪光灯功耗
mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
final double totalPower = app.sumPower();//对8项耗电统计进行累加
if (DEBUG && totalPower != 0) {
Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
makemAh(totalPower)));
}
// Add the app to the list if it is consuming power.
if (totalPower != 0 || u.getUid() == 0) {
//
// Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
//将app添加到 app list, WiFi, Bluetooth等,或其他用户列表
final int uid = app.getUid();
final int userId = UserHandle.getUserId(uid);
if (uid == Process.WIFI_UID) {//uid为wifi的情况
mWifiSippers.add(app);
} else if (uid == Process.BLUETOOTH_UID) {//uid为蓝牙的情况
mBluetoothSippers.add(app);
} else if (!forAllUsers && asUsers.get(userId) == null
&& UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
// We are told to just report this user's apps as one large entry.
//就目前来说 forAllUsers=true,不会进入此分支
List<BatterySipper> list = mUserSippers.get(userId);
if (list == null) {
list = new ArrayList<>();
mUserSippers.put(userId, list);
}
list.add(app);
} else {
mUsageList.add(app);//把app耗电加入到mUsageList
}
if (uid == 0) {
osSipper = app;// root用户,代表操作系统的耗电量
}
}
}
//app之外的耗电量
if (osSipper != null) {
// The device has probably been awake for longer than the screen on
// time and application wake lock time would account for. Assign
// this remainder to the OS, if possible.
mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs,
mRawUptimeUs, mStatsType);
osSipper.sumPower();
}
}
可以到看,软件类功耗类型是DrainType.APP,计算耗电量之前,先记录了耗电统计时长mTypeBatteryRealtimeUs。
mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
public long computeRealtime(long curTime, int which) {
switch (which) {
case STATS_SINCE_CHARGED:
return mRealtime + getRealtime(curTime);
case STATS_CURRENT:
return getRealtime(curTime);
case STATS_SINCE_UNPLUGGED:
return getRealtime(curTime) - mUnpluggedRealtime;
}
return 0;
}
mTypeBatteryRealtime会根据用户所传递进来的参数,去匹配得到时间参数,其中STATS_SINCE_CHARGED表示从上次充满电数据后;STATS_CURRENT表示现在时刻;STATS_SINCE_UNPLUGGED表示拔掉USB线后的时间。从这里表明充电时间的计算是从上一次充电后,拔掉设备到现在的耗电量统计。
然后列出8项耗电计算项,去计算每个的应用Uid对应在这些项上的电量损耗。8大项指标如下:
计算项 | 对应的类 |
---|---|
CPU功耗 | CpuPowerCalculator.java |
锁唤醒功耗 | WakelockPowerCalculator.java |
无线电功耗 | MobileRadioPowerCalculator.java |
WIFI功耗 | WifiPowerCalculator.java / WifiPowerEstimator.java |
蓝牙功耗 | BluetoothPowerCalculator.java |
传感器功耗 | SensorPowerCalculator.java |
相机功耗 | CameraPowerCalculator.java |
闪光灯功耗 | FlashlightPowerCalculator.java |
在计算完以上的各项数据后,会通过BatterySipper中的sumPower()方法去计算总的耗电量,该方法如下。
public double sumPower() {
return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
flashlightPowerMah + bluetoothPowerMah;
}
计算项 | 说明 |
---|---|
usage | 用户通用功耗(硬件计算) |
wifi | Wifi功耗 |
gps | Gps功耗 |
cup | Cup的功耗 |
sensor | 传感器功耗 |
mobileRadio | 无线电功耗 |
wakeLock | 锁唤醒功耗 |
camera | 相机功耗 |
flashLight | 闪光灯功耗 |
blueTooth | 蓝牙功耗 |
最后将计算的数据归类,分为:
- mWifiSippers.add(app): uid为wifi的耗电情况
- mBluetoothSippers.add(app): uid为蓝牙的耗电情况
- mUsageList.add(app): app耗电加入到mUsageList
- osSipper: root用户,代表操作系统的耗电量,app之外的wakelock耗电也计算该项
遍历完所有的应用的Uid后,软件类的耗电详情就统计完了,其计算总公式为:
SoftWare_Power = Uid_Power_1 + Uid_Power_2 + … + Uid_Power_n
值得注意的是,由于是通过Uid来统计的,如果有多个App共享Uid,那么这些应用的耗电详情会被统计在一起,计算公式如下:
Uid_Power = process_1_Power + … + process_N_Power ;
其中所有进程都是属于同一个uid。当同一的uid下,只有一个进程时,Uid_Power = process_Power;,而每个进程的耗电数据,就是通过上面九项数据之和得来的, 即:
process_Power = Gps功耗 +CPU功耗 + 锁唤醒功耗 + 无线电功耗 + WIFI功耗 + 蓝牙功耗 + 传感器功耗 + 相机功耗 + 闪光灯功耗。
注:用户通用功耗是属于硬件功耗,GPS功耗计算是在传感器里,android7.0以后才正式加入了蓝牙功耗统计。
下面以CPU功耗项为例,介绍软件功耗各项指标的计算方法。
注:每个子项的计算公式不尽相同,但是逻辑基本一致,所以不一一介绍,有兴趣的读者可以自行了解。
CPU功耗项是通过CpuPowerCalculator.java这个类来计算的,我们来看看这个类的初始化。
PowerCalculator mCpuPowerCalculator;
if (mCpuPowerCalculator == null) {
mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
}
mCpuPowerCalculator.reset();
public CpuPowerCalculator(PowerProfile profile) {
mProfile = profile;
}
首先,所有计算项类都是PowerCalculator的子类,其提供了calculateApp()抽象方法以及calculateRemaining()、reset()两个方法。其中calculateApp()用于计算App耗电数值,calculateRemaining()计算不属于App的耗电数值,reset()用以重置一些参数。
其次,在创建对象时,传入了PowerProfile对象,前面介绍了,在PowerProfile中会去读取power_profile.xml中所规定的功耗值数据,从而提供相应的基础功耗值,PowerCalculator所有子类会这个基础功耗值来计算出较为精准的功耗统计结果。
然后,让我们看下CPU功耗的计算过程,具体看如下代码。
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
//获取cpu在用户态和内核态的执行时长
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
// Aggregate total time spent on each cluster.
long totalTime = 0;
// 获取cpu簇的个数
final int numClusters = mProfile.getNumCpuClusters();
for (int cluster = 0; cluster < numClusters; cluster++) {
//获取每种cpu簇的主频等级的级数
final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < speedsForCluster; speed++) {
//获取Cpu不同频点下的运行时间
totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
}
}
//获取Cpu总共的运行时间
totalTime = Math.max(totalTime, 1);
double cpuPowerMaMs = 0;
//计算cup耗电量
for (int cluster = 0; cluster < numClusters; cluster++) {
final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < speedsForCluster; speed++) {
// 计算时间比率
final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
totalTime;
final double cpuSpeedStepPower = ratio * app.cpuTimeMs *
mProfile.getAveragePowerForCpu(cluster, speed);//根据cpu簇以及频率登记,获取单位时间内cup的功耗值
if (DEBUG && ratio != 0) {
Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
+ BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
}
// 总功耗计算
cpuPowerMaMs += cpuSpeedStepPower;
}
}
//换算成mAH
app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
+ BatteryStatsHelper.makemAh(app.cpuPowerMah));
}
// Keep track of the package with highest drain.
//追踪不同进程的耗电情况
double highestDrain = 0;
app.cpuFgTimeMs = 0;
final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
final int processStatsCount = processStats.size();
//统计同一个uid的不同进程的耗电情况
for (int i = 0; i < processStatsCount; i++) {
final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
final String processName = processStats.keyAt(i);
app.cpuFgTimeMs += ps.getForegroundTime(statsType);
final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
+ ps.getForegroundTime(statsType);
//App可以有多个packages和多个不同的进程,跟踪耗电最大的进程
// Each App can have multiple packages and with multiple running processes.
// Keep track of the package who's process has the highest drain.
if (app.packageWithHighestDrain == null ||
app.packageWithHighestDrain.startsWith("*")) {
highestDrain = costValue;
app.packageWithHighestDrain = processName;
} else if (highestDrain < costValue && !processName.startsWith("*")) {
highestDrain = costValue;
app.packageWithHighestDrain = processName;
}
}
//当Cpu前台时间 大于Cpu时间,将cpuFgTimeMs赋值为cpuTimeMs
// Ensure that the CPU times make sense.
if (app.cpuFgTimeMs > app.cpuTimeMs) {
if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) {
Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
}
// Statistics may not have been gathered yet.
app.cpuTimeMs = app.cpuFgTimeMs;
}
}
这里,我们看到,在calculateApp()方法中,通过mProfile对象获取了Cup簇的个数、每种cpu簇的主频等级的级数以及根据cpu簇以及频率登记,获取单位时间内cup的功耗值,并根据这些数值计算出CPU功耗值,也证实了前面的说法。
最后,给出CPU项计算公式:
cpuPower = ratio_1 * cpu_time * cpu_ratio_1_power + … +ratio_n * cpu_time * cpu_ratio_n_power
其中: ratio_i = cpu_speed_time/ cpu_speeds_total_time,(i=1,2,…,n,n为CPU频点个数)
硬件类
硬件类功耗,就没有分什么Uid了,直接是根据硬件属性来统计的,其方法如下:
private void processMiscUsage() {
//用户功耗
addUserUsage();
//通话功耗
addPhoneUsage();
//屏幕功耗
addScreenUsage();
//wifi功耗
addWiFiUsage();
//蓝牙功耗
addBluetoothUsage();
//Cup空闲功耗
addIdleUsage(); // Not including cellular idle power
// Don't compute radio usage if it's a wifi-only device
if (!mWifiOnly) {
// 移动无线功耗
addRadioUsage();
}
}
硬件功耗的子项共分为7项:
计算项 | 说明 | 类型 |
---|---|---|
UserUsage | 用户功耗 | DrainType.USER |
PhoneUsage | 手机通话功耗 | DrainType.PHONE |
ScreenUsage | 手机屏幕功耗 | DrainType.SCREEN |
WiFiUsage | Wifi功耗 | DrainType.WIFI |
BluetoothUsage | 蓝牙功耗 | DrainType.BLUETOOTH |
IdleUsage | CPU Idle功耗 | DrainType.IDLE |
RadioUsage | 移动无线功耗 | DrainType.CELL |
硬件功耗的总公式如下:
Hardware_Power = UserUsage + PhoneUsage + WiFiUsage + WiFiUsage + BluetoothUsage + CPU IdleUsage + RadioUsage;
下面以手机通话功耗为例,介绍硬件功耗子项的计算方式。
private void addPhoneUsage() {
// 获取手机通话时间
long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000;
// 通过通话时间来计算总电量
double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)// 获取单位时间内通话功耗
* phoneOnTimeMs / (60*60*1000);
if (phoneOnPower != 0) {
// 将该项数值添加到功耗列表中
addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
}
}
private BatterySipper addEntry(DrainType drainType, long time, double power) {
BatterySipper bs = new BatterySipper(drainType, null, 0);
bs.usagePowerMah = power;
bs.usageTimeMs = time;
// 计算该项总的功耗
bs.sumPower();
// 添加至功耗列表
mUsageList.add(bs);
return bs;
}
手机通话计算公式:
phone_powers = (phoneOnTimeMs * phoneOnPower) / (60* 60* 1000)
结语
根据上述所计算出来的软件与硬件的功耗详情,在应用中,我们可以通过反射去调用框架中的代码逻辑,从而获取功耗信息,再将这些数据进行一些处理,然后通过应用界面展示给客户。
try {
mStatsHelper = Class.forName("com.android.internal.os.BatteryStatsHelper");
Constructor<?>[] constructors = mStatsHelper.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
mConstructor = constructors[i];
if (mConstructor.getGenericParameterTypes().length == 3)
break;
}
mConstructor.setAccessible(true);
mStatsHelperObj = mConstructor.newInstance(context, false, false);
methodCreate = mStatsHelper.getDeclaredMethod(METHOD_BATTERY_CREATE, Bundle.class);
methodCreate.setAccessible(true);
methodCreate.invoke(mStatsHelperObj, bundle);
methodRefresh = mStatsHelper.getDeclaredMethod(METHOD_BATTERY_STATS_REFRESH, int.class, int.class);
methodRefresh.setAccessible(true);
methodRefresh.invoke(mStatsHelperObj, statsType, asUsers);
methodGetUsageList = mStatsHelper.getDeclaredMethod(METHOD_BATTERY_GETUSAGELIST);
methodGetUsageList.setAccessible(true);
batterySipperList = (List<BatterySipper>)methodGetUsageList.invoke(mStatsHelperObj);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "refreshStats exception: " +e);
} finally {
return batterySipperList;
}
至此本人所理解的电量统计部分基本上介绍完毕,但这并不是Android中电量统计的所有内容,还有很多细节需要读者去阅读源码细细体会,并且由于本人能力有限,有遗漏或错误的地方,还请批评指出,谢谢。