Android App耗電量統計
還沒有完成的,初稿
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() :硬體耗電量統計