通過 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 的文件內容。

該功能依次輸出內容項, 主要分為5大類:

  1. current log: kernel,system, event, radio;
  2. last log: kernel, system, radio;
  3. vm traces: just now, last ANR, tombstones
  4. dumpsys: all, checkin, app,batterystatus
  5. system info:cpu, memory, io等
  1. 系統build以及運行時長等相關信息;
  2. 內存/CPU/進程等信息;
  3. kernel log
  4. lsof、map及Wait-Channels;
  5. system log
  6. event log
  7. radio log;
  8. vm traces
    1. VM TRACES JUST NOW (/data/anr/traces.txt.bugreport) (抓bugreport時主動觸發)
    2. VM TRACES AT LAST ANR (/data/anr/traces.txt) (存在則輸出)
    3. TOMBSTONE (/data/tombstones/tombstone_xx) (存在這輸出)
  9. network相關信息;
  10. last kernel log;
  11. last system log;
  12. ip相關信息;
  13. 中斷向量表
  14. property以及fs等信息
  15. last radio log;
  16. Binder相關信息;
  17. dumpsys all:
  18. dumpsys checkin相關:
    • dumpsys batterystats電池統計;
    • dumpsys meminfo內存
    • dumpsys netstats網絡統計;
    • dumpsys procstats進程統計;
    • dumpsys usagestats使用情況;
    • dumpsys package.
  19. dumpsys app相關
    • dumpsys activity;
    • dumpsys activity service all;
    • dumpsys activity provider all.
  • 電量統計信息起始,包含 reset 時間,進程信息等

  • 下面是距離上次充電后的數據統計:

  • 每個 APP 電量使用情況,通過 Uid 來標識APP

 可以通過關鍵字下面的關鍵字來尋找相關信息。

DUMP OF SERVICE    

通過該關鍵字可以查到 wifi, 網絡, activities 等等的信息。

Tips: bugreport幾乎涵蓋整個系統信息,內容非常長,每一個子項都以------ xxx ------開頭。 例如APP ACTIVITIES的開頭便是 ------ APP ACTIVITIES (dumpsys activity all) ------,其中括號內的便是輸出該信息指令,即dumpsys activity all,還有可能是內容所在節點,各個子項目類似的規律

 

參考文章

1、開發者大殺器 —— Battery Historian,刨根問底,揪出 Android App 耗電的元凶代碼

2、Android耗電量 - bugreport & Battery Historian


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM