通過 Battery Historian 工具分析 Android APP 耗電情況
電量統計模組概述
Android 從兩個層面統計電量的消耗,分別為 軟體排行榜 及 硬體排行榜。它們各有自己的耗電榜單,軟體排行榜為機器中每個 App 的耗電榜單,硬體排行榜則為各個硬體的耗電榜單。這兩個排行榜的統計是互為獨立,互不干擾的。
具體的說,耗電資訊在 設定 -> 電量
中能夠非常直觀的看到。注意,Android 所有功耗統計都是通過程式碼估算,沒有積體電路參與彙報。準確度取決於廠商 ROM 所提供的 power_profile.xml
檔案。由於不同廠商 power_profile.xml
準確度及原始碼有差異,因此不同手機、不同版本的資料可能有較大差異。
power_profile.xml
直接影響統計的準確度,並且此檔案無法通過應用修改。再次強調,Android 耗電估算沒有硬體的參與,全靠程式碼估算。
power_profile.xml
檔案位於原始碼下的 /framework/base/core/res/res/xml/power_profile.xml
,部分內容展示如下:
<item name="radio.scanning">0.1</item> <!-- cellular radio scanning for signal, ~10mA --> <item name="gps.on">0.1</item> <!-- ~50mA --> <!-- Current consumed by the radio at different signal strengths, when paging --> <array name="radio.on"> <!-- Strength 0 to BINS-1 --> <value>0.2</value> <!-- ~2mA --> <value>0.1</value> <!-- ~1mA --> </array> </array> <!-- Different CPU speeds as reported in /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state --> <array name="cpu.speeds"> <value>400000</value> <!-- 400 MHz CPU speed --> </array> <!-- Current when CPU is idle --> <item name="cpu.idle">0.1</item> <!-- Current at each CPU speed, as per 'cpu.speeds' --> <array name="cpu.active"> <value>0.1</value> <!-- ~100mA --> </array> <array name="wifi.batchedscan"> <!-- mA --> <value>.0002</value> <!-- 1-8/hr --> <value>.002</value> <!-- 9-64/hr --> <value>.02</value> <!-- 65-512/hr --> <value>.2</value> <!-- 513-4,096/hr --> <value>2</value> <!-- 4097-/hr --> </array>
這就是在硬體層面統計時,直接參與運算的引數。無論是軟體耗電統計還是硬體耗電統計,都通過 BatteryStatsHelper
來進行彙總。BatteryStatsHelper
位於 /framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java
下。
軟體耗電統計
在 BatteryStatsHelper.java
中,有這麼一個方法:
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); mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); final double totalPower = app.sumPower(); if (DEBUG && totalPower != 0) { Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(), makemAh(totalPower))); } } ... // code }
processAppUsage()
方法中,一個應用的總功耗在這裡體現出來了:
-
cpu
-
Wakelock(保持喚醒鎖)
-
無線電(2G/3G/4G)
-
WIFI
-
藍芽
-
感測器
-
相機
-
閃光燈
這些資料,將決定著你的應用在耗電排行榜中的位置,以及是否給予使用者警告高耗電。這些警告對於應用來說可能是致命的,使用者可能因此而解除安裝應用。
應用總功耗是上述八個統計值的和。這八個統計器同繼承自 PowerCalculator.java
具體來說,這八個耗電計算器的演算法分別如下:
耗電統計概述就如上所述。總的來說並不複雜,通過聚合八種不同方式的消耗,來得出總的耗電量,並給予使用者展示。
battery-historian
概述
Battery Historian ,是谷歌出品的耗電分析器。通過 Battery Historian,可將匯出的bugreport
檔案視覺化。在第一代的 Battery Historian 中,google 使用了 python 作為資料解析工具。拿到 bugreport
檔案後,通過終端執行 python 來生成視覺化的 html 檔案。這種方法使用起來較為麻煩,而且無法部署到伺服器。因此在第二代 Battery Historian,google 選擇了使用 docker 容器。現在完全不推薦使用第一代 Battery Historian,已經許久沒有維護了,而且功能過於簡陋。
需要注意的是 battery-historian 在使用時候不能在充電,同時確保裝置執行的 Android 版本是 5.0 及以上。
通過上面的描述,對 battery-historian 的功能有個大概的瞭解,下面進入到實戰。
獲取 bugreports
battery-historian 雖然功能強大,但是也是需要先提供資料的,其資料的獲取需要我們手動操作。下面介紹如何獲取 bugreports。
- 1. 電腦連線上手機,斷開adb服務,adb作為一種連線的方式,有可能被其他的程式佔用,所以我們做電量記錄時要避免開啟很多可能衝突的東西
adb kill-server
- 2. 重啟adb服務
adb devices || adb start-server
-
3. Android也不記錄特定於應用程式的使用者空間wakelock轉換的時間戳。如果您希望Historian在時間線上顯示關於每個單獨喚醒鎖的詳細資訊,則應在開始實驗之前使用以下命令啟用完整喚醒鎖報告:
adb shell dumpsys batterystats --enable full-wake-history
-
4. 採集報告前將battery統計狀態重置,重置命令結束後斷開usb,測試結束後用獲取報告命令匯出統計檔案包
adb shell dumpsys batterystats --reset
- 5. 匯出電量,對於 7.0 系統以上的裝置運用:
adb bugreport bugreport.zip adb bugreport > $HOME/Documents/bugreport.zip // 指定到對應目錄下,具體分機型,可能會有些不一樣
6.0 或更低版本:
adb bugreport > bugreport.txt
資料分析
獲取到資料後,接下去就是對資料進行分析了。
使用 battery-historian 是需要搭建環境的,由於搭建環境比較複雜, 可以借用別人搭好的線上環境:https://bathist.ef.lc/,點選開啟後,上傳資料,就會得到詳細的結果。
如圖所示Battery Historian圖表的一個例子:
其中標號的意義是:
-
標號1:從下拉列表中新增其他指標;
-
標號2:將滑鼠懸停在資訊圖示上可以檢視有關每個指標的詳細資訊,包括圖表中使用的不同顏色代表意義的介紹;
-
標號3:將滑鼠懸停在某個條目上可以檢視該指標的更多詳細資訊,以及時間線上特定點的耗電量資訊;
這是整個手機狀態圖,包括手機電量,CPU 使用時長,wifi 訊號的強度,手機溫度變化等等,都在上面詳細的用圖表展示出來了。
Battery Historian除了能夠提供巨集觀的系統層面的資訊,還能夠提供針對指定App的視覺化資料和表格資訊,這表格主要資訊包括:
-
Device estimated power use等基本資訊
-
Networks Information:app網路資訊
-
Wakelocks:喚醒鎖資訊,一般和業務強相關
-
Services:服務資訊,檢視App開啟的services資訊
-
Process info:程序資訊
Battery Historian圖表下為資料分析,包括三個Tab,如下圖所示,可以檢視App更多資訊:
其中,標號所代表的意義是:
-
標號1:System Stats 分組包含系統級別的資料,比如螢幕亮度等。這一欄顯示了系統發生的總體情況,可以用來測試是否存在外部影響事件;
-
標號2:App Stats分組包含針對指定APP的詳細資訊;
-
標號3:可以根據不同的分類標準對APP進行排序;
-
標號4:在下拉列表中選擇指定的APP後可在App Stats中檢視具體資訊,App Stats所展示的都是所選定App產生的資料,不會受到外部因素的影響;
下面看看具體某個手機的資料,比如百度APP應用的資料,在右邊選擇對應的 APP,左邊就會展示當前 APP 的電量,網路等情況。
bugreport 檔案分析
前面是通過 Battery History 對 bugreport 的檔案進行了分析,那如果我們想自己分析呢?因此,在這裡有必要了解下 bugreport 的檔案內容。
- 電量統計資訊起始,包含 reset 時間,程序資訊等
- 下面是距離上次充電後的資料統計:
- 每個 APP 電量使用情況,通過 Uid 來標識APP
可以通過關鍵字下面的關鍵字來尋找相關資訊。
DUMP OF SERVICE
通過該關鍵字可以查到 wifi, 網路, activities 等等的資訊。
參考文章
1、開發者大殺器 —— Battery Historian,刨根問底,揪出 Android App 耗電的元凶程式碼
2、Android耗電量 - bugreport & Battery Histo