這是一篇講述應(yīng)用耗電的文章,圍繞 Android 電量采集機(jī)制及第二代Battery Historian分析工具講述。文從數(shù)據(jù)采集、導(dǎo)出、環(huán)境搭建、解讀報(bào)告的角度出發(fā),從細(xì)節(jié)講解整個(gè)流程。和大談概念的文章不同,這里將進(jìn)行實(shí)際操作及分析。
電量統(tǒng)計(jì)模塊概述
Android 從兩個(gè)層面統(tǒng)計(jì)電量的消耗,分別為軟件排行榜及硬件排行榜。它們各有自己的耗電榜單,軟件排行榜為機(jī)器中每個(gè) App 的耗電榜單,硬件排行榜則為各個(gè)硬件的耗電榜單。這兩個(gè)排行榜的統(tǒng)計(jì)是互為獨(dú)立,互不干擾的。
** 此處主要講述軟件層面的統(tǒng)計(jì)。**
具體的說,耗電信息在設(shè)置 -> 電量中能夠非常直觀的看到。注意,Android 所有功耗統(tǒng)計(jì)都是通過代碼估算,沒有集成電路參與匯報(bào)。準(zhǔn)確度取決于廠商 ROM 所提供的power_profile.xml文件。由于不同廠商power_profile.xml準(zhǔn)確度及源碼有差異,因此不同手機(jī)、不同版本的數(shù)據(jù)可能有較大差異。
power_profile.xml直接影響統(tǒng)計(jì)的準(zhǔn)確度,并且此文件無法通過應(yīng)用修改。再次強(qiáng)調(diào),Android 耗電估算沒有硬件的參與,全靠代碼估算。
power_profile.xml文件位于源碼下的/framework/base/core/res/res/xml/power_profile.xml,部分內(nèi)容展示如下:
<itemname="radio.scanning">0.1</item><!-- cellular radio scanning for signal, ~10mA --><itemname="gps.on">0.1</item><!-- ~50mA --><!-- Current consumed by the radio at different signal strengths, when paging --><arrayname="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 --><arrayname="cpu.speeds"><value>400000</value><!-- 400 MHz CPU speed --></array><!-- Current when CPU is idle --><itemname="cpu.idle">0.1</item><!-- Current at each CPU speed, as per 'cpu.speeds' --><arrayname="cpu.active"><value>0.1</value><!-- ~100mA --></array><arrayname="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>
這就是在硬件層面統(tǒng)計(jì)時(shí),直接參與運(yùn)算的參數(shù)。無論是軟件耗電統(tǒng)計(jì)還是硬件耗電統(tǒng)計(jì),都通過BatteryStatsHelper來進(jìn)行匯總。BatteryStatsHelper位于/framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java下。
軟件耗電統(tǒng)計(jì)
在BatteryStatsHelper.java中,有這么一個(gè)方法:

processAppUsage()方法中,一個(gè)應(yīng)用的總功耗在這里體現(xiàn)出來了:
cpu
Wakelock(保持喚醒鎖)
無線電(2G/3G/4G)
WIFI
藍(lán)牙
傳感器
相機(jī)
閃光燈
這些數(shù)據(jù),將決定著你的應(yīng)用在耗電排行榜中的位置,以及是否給予用戶警告高耗電。這些警告對(duì)于應(yīng)用來說可能是致命的,用戶可能因此而卸載應(yīng)用。
應(yīng)用總功耗是上述八個(gè)統(tǒng)計(jì)值的和。這八個(gè)統(tǒng)計(jì)器同繼承自PowerCalculator.java
具體來說,這八個(gè)耗電計(jì)算器的算法分別如下:

耗電統(tǒng)計(jì)概述就如上所述??偟膩碚f并不復(fù)雜,通過聚合八種不同方式的消耗,來得出總的耗電量,并給予用戶展示。
耗電數(shù)據(jù)的采集
數(shù)據(jù)的采集是機(jī)器單方面的行為,不需要依賴第三方的輔助,因?yàn)檫@是 Android 系統(tǒng)級(jí)的功能。但在這之前,需要做一些準(zhǔn)備。請(qǐng)事先打開開發(fā)者模式。USB 接入計(jì)算機(jī)。在終端中執(zhí)行:
adb shell dumpsys batterystats --enable full-wake-history
默認(rèn)情況下,喚醒(wake)數(shù)據(jù)是不會(huì)被采集的,因此我們需要將其啟用。如果采集的不是全量 wake up 數(shù)據(jù),在分析階段則不能很好的觀測數(shù)據(jù)。
隨后終端執(zhí)行:
adb shell dumpsys batterystats --reset
此命令會(huì)清空歷史采集的信息。
最后,拔出 USB。
最后,拔出 USB。
最后,拔出 USB。
重要的事說三遍!
現(xiàn)在,耗電統(tǒng)計(jì)已經(jīng)開始了。沒錯(cuò),耗電統(tǒng)計(jì)就是一直開著的,并且無法關(guān)閉:這是一個(gè)系統(tǒng)級(jí)別的功能。
為什么要拔出 USB?因?yàn)槿绻阋恢辈逯?USB ,如果電充滿了,你的數(shù)據(jù)會(huì)被清空的。Batterystats 只會(huì)記錄最后一次充滿電后的記錄,因此強(qiáng)烈建議先把電充滿,完成以上操作后,拔出 USB 電源。
接下來,就像日常使用手機(jī)一樣,操作你想要統(tǒng)計(jì)的應(yīng)用。耗電記錄器會(huì)在后臺(tái)統(tǒng)計(jì)整臺(tái)機(jī)子所有的耗電情況。沒錯(cuò),不需要事先指定目標(biāo) App ,所有 App 都會(huì)被統(tǒng)計(jì)。這也說明,任何人都能夠統(tǒng)計(jì)任何已安裝的應(yīng)用。因此,除了統(tǒng)計(jì)自家 App ,也能用于統(tǒng)計(jì)競品。
當(dāng)你覺得操作得差不多了,連接到 USB,終端執(zhí)行:
adb bugreport bugreport.zip
如果是 Android 6.0 及以下執(zhí)行:
adb bugreport > bugreport.txt
bugreport.txt就是記載著整臺(tái)手機(jī)耗電信息的源數(shù)據(jù)。
最后終端執(zhí)行:
adb shell dumpsys batterystats --disable full-wake-history
不要忘了關(guān)閉全量記錄喚醒。保持開啟會(huì)造成性能問題,除非在電量收集階段,否則建議保持關(guān)閉。
接下來,搭建分析環(huán)境:Battery Historian。
搭建 Battery Historian & 上傳 bugreport
Battery Historian,是谷歌出品的耗電分析器。通過 Battery Historian,可將導(dǎo)出的bugreport文件可視化。在第一代的 Battery Historian 中,google 使用了 python 作為數(shù)據(jù)解析工具。拿到bugreport文件后,通過終端執(zhí)行 python 來生成可視化的 html 文件。這種方法使用起來較為麻煩,而且無法部署到服務(wù)器。因此在第二代 Battery Historian,google 選擇了使用 docker 容器。現(xiàn)在完全不推薦使用第一代 Battery Historian,已經(jīng)許久沒有維護(hù)了,而且功能過于簡陋。
我在此演示 Mac 環(huán)境的搭建,其他操作系統(tǒng)大同小異,可參考 Battery Historian 官方教程。
首先下載Docker并安裝。
啟動(dòng) Docker。點(diǎn)擊上方狀態(tài)欄 Docker 圖標(biāo),如圖所示 "Docker is running" 則表示啟動(dòng)成功。

docker
下一步,啟動(dòng)終端,執(zhí)行:
docker run -p 9998:9999 gcr.io/android-battery-historian:2.1 --port 9999
如果您未曾運(yùn)行過此 Docker 鏡像,將會(huì)自動(dòng)下載此鏡像并安裝。9998端口指的是映射到你的本地端口,這意味著,當(dāng)鏡像執(zhí)行后,可通過127.0.0.1:9998訪問此鏡像。你可以自行更改此端口。等待鏡像下載完成,再次執(zhí)行上一條命令。如果成功,將輸出如下信息:
?~docker run-p9998:9999gcr.io/android-battery-historian:2.1--port99992017/06/0410:24:13Listeningon port:9999
鏡像的9999端口已被監(jiān)聽,并映射到實(shí)體機(jī)器的9998端口。分析平臺(tái)部署完成了,開始上傳bugreport文件進(jìn)行分析。
現(xiàn)在,訪問127.0.0.1:9998,成功打開 Battery Historian 分析平臺(tái):

除了單一上傳bugreport文件外,還支持更多的分析文件上傳。此外,還能對(duì)比兩份bugreport文件。這對(duì)比功能簡直是神器。這里就不展開述說了,直接上傳bugreport.txt。
上傳后效果如下:

現(xiàn)在一起來看看怎么使用 Battery Historian 分析bugreport文件。
鳥瞰 Battery Historian
再次強(qiáng)調(diào),bugreport文件包含了整臺(tái)手機(jī)運(yùn)行狀況,并非單一某個(gè) app,因此查看圖表時(shí)要特別注意,數(shù)據(jù)所展示的是當(dāng)前選中的 app 數(shù)據(jù)還是全部 app 的疊加。
現(xiàn)在,我用微信作為分析目標(biāo)。選中 com.tencent.mm。

現(xiàn)在,這張圖表第一個(gè)坑爹的地方出現(xiàn)了。選中目標(biāo)包名前后,圖標(biāo)數(shù)據(jù)會(huì)有些不一樣的地方。選中前:

選中后:

注意到加粗的 Top app , Activity Manager proc , JobScheduler 了嗎?這幾個(gè)數(shù)據(jù)在選中后,圖表數(shù)據(jù)會(huì)變?yōu)閮H有當(dāng)前選中 app 的數(shù)據(jù),而其他數(shù)據(jù)仍然是整臺(tái)機(jī)子的全量數(shù)據(jù)。一不小心,還能坑你很多次。此處是初次使用 Battery Historian 需要特別注意的地方。用鼠標(biāo)指向圖標(biāo),可粗略地觀察數(shù)據(jù)的變化。
數(shù)據(jù)分析分為三個(gè) Tables,分別是System Stats,History Stats,App Stats。System Stats 和 App Stats 是重點(diǎn)觀測和分析對(duì)象。
System Stats 包含了機(jī)子整體概況,包括整臺(tái)機(jī)子在這段期間消耗了多少電量,所有 app 使用 Wakelocks、JobScheduler、CPU、Wifi、傳感器等等一切的所有情況。
接下來則是 App Stats,所有的優(yōu)化都是為了此處的數(shù)據(jù)而努力。
讀懂 App Stats
App Stats 所展示的都是所選定包名所產(chǎn)生的數(shù)據(jù),不會(huì)受到外部因素的影響。
Misc Summary 部分概述了所選定 app 在收集階段的活動(dòng)概況:

如上所述,
電量消耗占用了總消耗的3.94%;
前臺(tái)運(yùn)行了 3 小時(shí) 34 分鐘;
震動(dòng)了 8 次,共 225 毫秒;
CPU 用戶態(tài)時(shí)間 22 分 33 秒;
Alarm 喚醒 40 次。
來看一個(gè)具體的數(shù)據(jù):查看 App Stats 下的 Wakelocks 數(shù)據(jù)區(qū)域:

注意,顯示為 WakerLock:xxxxxxx 是混淆導(dǎo)致。您自己的開發(fā)包不會(huì)存在此情況。
你還記不記得 App 的耗電排行榜是如何計(jì)算的?上面這些數(shù)據(jù)都會(huì)影響文章開頭所提到的耗能計(jì)算公式。
舉個(gè)例子,假如你一直持有一個(gè) WakePowerLock,但你什么都沒干 —— 這時(shí)候其實(shí)是不會(huì)產(chǎn)生真正耗電的,對(duì)吧。但因?yàn)槟愠钟幸粋€(gè) Lock,公式就是這么算的:wakeLockTime * wakeLockPower。即使你啥也沒干,Android 系統(tǒng)也認(rèn)為你在耗電,這時(shí)候就很吃虧了。
再來看看 Alarm 喚醒(App Stats 下的 Wakeup alarm info):

查看你自家的 app,可能會(huì)驚訝的發(fā)現(xiàn)有如此之多不必要的喚醒。Wakeup Alarm 和 Scheduled Job 可能被某些廠商用于檢測頻繁后臺(tái)喚醒,并向用戶展示該信息。
最后,再來看看 Sensor Use 部分:

看看您自家的應(yīng)用是否有過多的傳感器調(diào)用?如非必要,能復(fù)用上一次的 GPS 數(shù)據(jù)嗎?這些所有的資源消耗,都會(huì)被算入能耗當(dāng)中。當(dāng)您的 app 在耗電榜上屢次得冠,就離卸載不遠(yuǎn)了。
?最后
大多數(shù)情況下,優(yōu)化的效果會(huì)是驚人的。這當(dāng)中可能會(huì)遇到一些阻礙,包括產(chǎn)品需求的沖突。盡管試著去協(xié)商,試著向傳達(dá)能耗所帶來的代價(jià)。
本文為轉(zhuǎn)載文章:
作者:Abel_嘉俊
鏈接:http://m.itdecent.cn/p/27ba2759b221
來源:簡書