Android App耗电量统计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16318981/article/details/54616459
还没有完成的,初稿

App耗电量统计:源码

  • PowerUsageSummary.java 继承PowerUsageBase.java类
BatteryHistoryPreference类--sp 获取耗电量历史(读取sp文件)--sp文件数据来自xml文件(power_usage_summary.xml)
PreferenceGroup类--统计所有App耗电量同history

refreshStats()方法刷新耗电量状态:内部有以下几个类(BatteryStatsHelper.java)获取
1.PowerProfile:提供部件电流数值。
2.BatteryStats:App各部件运行时间。(获取封装在BatteryStatsHelper类中具体看下面)
final BatteryStats stats = mStatsHelper.getStats();
3.根据BatteryStatsHelper类中统计
- App耗电量统计:processAppUsage()  
- 硬件耗电量统计:processMiscUsage()
获取耗电量更新SP数据: mAppListGroup.addPreference(pref);
  • PowerProfile.java
    (1)Android部件电流信息存于:power_profile.xml

(2)每个OEM厂商有私有power_profile.xml

(3)PowerProfile读取power_profile.xml,并提供API访问部件电流数值。

/**
 * Reports power consumption values for various device activities. Reads values from an XML file.
 * Customize the XML file for different devices.
 * [hidden]
 */
38public class PowerProfile {

 private void readPowerValuesFromXml(Context context) {
    int id = com.android.internal.R.xml.power_profile;//xml文件power_profile.xml
    //下面就是xml解析了根据下面定义节点获取值:
    //private static final String TAG_DEVICE = "device";
    //private static final String TAG_ITEM = "item";
    //private static final String TAG_ARRAY = "array";
    //private static final String TAG_ARRAYITEM = "value";
    //private static final String ATTR_NAME = "name";
    ....
最后添加数据到sPowerMap (一个HashMap) sPowerMap.put(key, (double) value); key是各部件名称。value是各部件的电流值
}
}

//power_profile.xml 各部件定义的电流值 屏幕 cpu 蓝牙 wifi gps等等

<device name="Android">  
  <!-- Most values are the incremental current used by a feature,  
       in mA (measured at nominal voltage).  
       The default values are deliberately incorrect dummy values.  
       OEM's must measure and provide actual values before  
       shipping a device.  
       Example real-world values are given in comments, but they  
       are totally dependent on the platform and can vary  
       significantly, so should be measured on the shipping platform  
       with a power meter. -->  
  <item name="none">0</item>  
  <item name="screen.on">100</item>  <!-- ~200mA -->  
  <item name="screen.full">200</item>  <!-- ~300mA -->  
  <item name="bluetooth.active">90.5</item> <!-- Bluetooth data transfer, ~10mA -->  
  <item name="bluetooth.on">2.5</item>  <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->  
  <item name="wifi.on">1.25</item>  <!-- ~3mA -->  
  <item name="wifi.active">130</item>  <!-- WIFI data transfer, ~200mA -->  
  <item name="wifi.scan">100</item>  <!-- WIFI network scanning, ~100mA -->  
  <item name="dsp.audio">30.5</item> <!-- ~10mA -->  
  <item name="dsp.video">72.5</item> <!-- ~50mA -->  
  <item name="radio.active">135</item> <!-- ~200mA -->  
  <item name="radio.scanning">5.3</item> <!-- cellular radio scanning for signal, ~10mA -->  
  <item name="gps.on">30</item> <!-- ~50mA -->  
  <!-- Current consumed by the radio at different signal strengths, when paging -->  
  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->  
      <value>3.5</value> <!-- ~2mA -->  
      <value>2.4</value> <!-- ~1mA -->  
  </array>  
  <!-- Different CPU speeds as reported in  
       /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->  
  <array name="cpu.speeds">  
      <value>624000</value> <!-- 624 MHz CPU speed -->  
      <value>699563</value> <!-- 699 MHz CPU speed -->  
      <value>799500</value> <!-- 799 MHz CPU speed -->  
      <value>899438</value> <!-- 899 MHz CPU speed -->  
      <value>999375</value> <!-- 999 MHz CPU speed -->  
      <value>1099313</value> <!-- 1099 MHz CPU speed -->  
      <value>1199250</value> <!-- 1199 MHz CPU speed -->  
      <value>1299188</value> <!-- 1299 MHz CPU speed -->  
      <value>1399125</value> <!-- 1399 MHz CPU speed -->  
      <value>1499063</value> <!-- 1499 MHz CPU speed -->  
      <value>1599000</value> <!-- 1599 MHz CPU speed -->  
  </array>  
  <!-- Current when CPU is idle -->  
  <item name="cpu.idle">2.2</item>  
  <!-- Current at each CPU speed, as per 'cpu.speeds' -->  
  <array name="cpu.active">//各个cpu频段的功耗  
      <value>54</value>  
      <value>63</value>  
      <value>72</value>  
      <value>80</value>  
      <value>90</value>  
      <value>100</value>  
      <value>109</value>  
      <value>115</value>  
      <value>121</value>  
      <value>127</value>  
      <value>135</value>  
  </array>  
  <!-- This is the battery capacity in mAh (measured at nominal voltage) -->  
  <item name="battery.capacity">2000</item>  
</device>  
  • BatteryStats.java 抽象类implements Parcelable(android 序列化)
/**
 * 统计电池各部件使用情况,时间以微秒为单位
 * A class providing access to battery usage statistics, including information on
 * wakelocks, processes, packages, and services.  All times are represented in microseconds
 * except where indicated otherwise. 
 * @hide
 */
public abstract class BatteryStats implements Parcelable {
    private static final String TAG = "BatteryStats";

    private static final boolean LOCAL_LOGV = false;

   /** @hide */
    public static final String SERVICE_NAME = "batterystats";
    ....
}
  • BatteryStatsHelper.java Android BatteryStatsHelper深入理解(and5.1)
    作用:主要是统计各个应用,多用户的每个用户,以及蓝牙,屏幕等耗电统计
    BatteryStatsHelper 的实例在PowerUsageBase类初始化:
@Override
    public void onAttach(Activity activity) {
       super.onAttach(activity);
       mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE);
       mStatsHelper = new BatteryStatsHelper(activity, true);//实例化
   }

BatteryStatsHelper.java 类内部方法

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)); //aidl服务 SERVICE_NAME = "batterystats";;
    mPowerProfile = new PowerProfile(mContext);  //从power_profile.xml读取的各个器件的电源消耗参数具体查看上面详解这个类的链接
} 

在PowerUsageSummary类分析获取App各部件运行时间 调用BatteryStats stats = mStatsHelper.getStats();

public BatteryStats getStats() {
       if (mStats == null) {
            load(); //如果mStats为空 调用load方法否则返回mStats
        }
        return mStats;
    }

private void load() {
        if (mBatteryInfo == null) {//mBatteryInfo空判断在Create里面初始化了
            return;
        }
        mStats = getStats(mBatteryInfo); //调用getStats,返回BatteryStatsImpl实现类
        if (mCollectBatteryBroadcast) {
            mBatteryBroadcast = mContext.registerReceiver(null,
                    new IntentFilter(Intent.ACTION_BATTERY_CHANGED));//注册监听电量改变广播
        }
    }

private static BatteryStatsImpl getStats(IBatteryStats service) {
        try {
            ParcelFileDescriptor pfd = service.getStatisticsStream();//BatteryStatsService服务类:收集所有消耗电池信息 调用updateExternalStatsSync()从外部获取数据来源(无线控制器,bluetooth芯片组)和更新batterystats信息。
            if (pfd != null) {
                try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {//下面读取序列化反序列化获取batterystats数据
                    byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
                    Parcel parcel = Parcel.obtain();
                    parcel.unmarshall(data, 0, data.length);
                    parcel.setDataPosition(0);
                    BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
                            .createFromParcel(parcel);
                    return stats;
                } catch (IOException e) {
                   Log.w(TAG, "Unable to read statistics stream", e);
                }
            }
        } catch (RemoteException e) {
            Log.w(TAG, "RemoteException:", e);
        }
        return new BatteryStatsImpl();
    }

接下来就是refreshState更新电池最新状态的,statsType是指充电状态还是非充电状态,asUsers指的是userId(多用户)

public void refreshStats(int statsType, List<UserHandle> asUsers) {  
    final int n = asUsers.size();  
    SparseArray<UserHandle> users = new SparseArray<UserHandle>(n);  
    for (int i = 0; i < n; ++i) {  
        UserHandle userHandle = asUsers.get(i);  
        users.put(userHandle.getIdentifier(), userHandle);  
    }  
    refreshStats(statsType, users);  
}  

/** 
 * Refreshes the power usage list. 
 */  
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {  
    refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,  
            SystemClock.uptimeMillis() * 1000);  
} 

refreshStats方法:processAppUsage()计算每个uid的耗电情况, processMiscUsage();//计算比如屏幕、wifi、蓝牙等耗电

 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
           long rawUptimeUs) {
        ...
        ...
        ...
        processAppUsage(asUsers);//计算每个uid的耗电情况包含:CPU Wakelock WIFI 蓝牙 传感器 Camera FlashLight MobileRadio
        ...
        ...
        processMiscUsage();//计算比如屏幕、wifi、蓝牙等耗电
        ...
        ...
        }

processAppUsage(asUsers){
 mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App Cpu耗电量计算
 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App Wakelock耗电量计算
 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App MobileRadio耗电量计算
 mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App WIFI耗电量计算
 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App 蓝牙耗电量计算
 mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App 传感器耗电量计算
 mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App Camera耗电量计算
 mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);//每个App Flashlight耗电量计算
 }

 private void processMiscUsage() {
   addUserUsage();
   addPhoneUsage();
   addScreenUsage();
   addWiFiUsage();
   addBluetoothUsage();
   addIdleUsage(); // Not including cellular idle power
   // Don't compute radio usage if it's a wifi-only device
   if (!mWifiOnly) {
      addRadioUsage();
     }
   }
  • BatteryEntry.java

看源码解释:相当于bean耗电量数据(package name , icon , iconId; // For passing to the detail screen.)
内部维护一个 NameAndIconLoader线程去加载BatteryEntry数据。

/**
41 * Wraps the power usage data of a BatterySipper with information about package name
42 * and icon image.
43 */

构造函数统计硬件软件App信息:

 public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) {
130        sHandler = handler;
131        this.context = context;
132        this.sipper = sipper;
133        switch (sipper.drainType) {
134            case IDLE:
135                name = context.getResources().getString(R.string.power_idle);
136                iconId = R.drawable.ic_settings_phone_idle;
137                break;
138            case CELL:
139                name = context.getResources().getString(R.string.power_cell);
140                iconId = R.drawable.ic_settings_cell_standby;
141                break;
142            case PHONE://通话
143                name = context.getResources().getString(R.string.power_phone);
144                iconId = R.drawable.ic_settings_voice_calls;
145                break;
146            case WIFI://wifi
147                name = context.getResources().getString(R.string.power_wifi);
148                iconId = R.drawable.ic_settings_wireless;
149                break;
150            case BLUETOOTH://蓝牙
151                name = context.getResources().getString(R.string.power_bluetooth);
152                iconId = R.drawable.ic_settings_bluetooth;
153                break;
154            case SCREEN://屏幕
155                name = context.getResources().getString(R.string.power_screen);
156                iconId = R.drawable.ic_settings_display;
157                break;
158            case FLASHLIGHT:
159                name = context.getResources().getString(R.string.power_flashlight);
160                iconId = R.drawable.ic_settings_display;
161                break;
162            case APP:
163                name = sipper.packageWithHighestDrain;
164                break;
165            case USER: {
166                UserInfo info = um.getUserInfo(sipper.userId);
167                if (info != null) {
168                    icon = Utils.getUserIcon(context, um, info);
169                    name = Utils.getUserLabel(context, info);
170                } else {
171                    icon = null;
172                    name = context.getResources().getString(
173                            R.string.running_process_item_removed_user_label);
174                }
175            } break;
176            case UNACCOUNTED:
177                name = context.getResources().getString(R.string.power_unaccounted);
178                iconId = R.drawable.ic_power_system;
179                break;
180            case OVERCOUNTED:
181                name = context.getResources().getString(R.string.power_overcounted);
182                iconId = R.drawable.ic_power_system;
183                break;
184            case CAMERA:
185                name = context.getResources().getString(R.string.power_camera);
186                iconId = R.drawable.ic_settings_camera;
187                break;
188        }
189        if (iconId > 0) {
190            icon = context.getDrawable(iconId);
191        }
192        if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
193            getQuickNameIconForUid(this.sipper.uidObj.getUid());
194        }
195    }

总结:

1.Android电量消耗统计在:BatteryStatsHelper类
2.getPowerProfile() 获取PowerProfile,PowerProfile类是(读取power_profile.xml文件)获取Android各部件电流值
3.getStats()获取BatteryStats(App各部件运行时间微秒为单位),通过BatteryStatsService服务类:收集所有消耗电池信息 调用updateExternalStatsSync()从外部获取数据来源(无线控制器,bluetooth芯片组)和更新batterystats信息。
4.refreshState更新电池最新状态
- processAppUsage() :App耗电量统计
- processMiscUsage() :硬件耗电量统计

参考:http://www.cnblogs.com/hyddd/p/4402621.html

没有更多推荐了,返回首页