Android電量優化全解析
電量優化一直是個老生常談的話題,關於這塊的文章已經有很多了,最近也在做這塊東西,所以結合自己的理解寫下這篇文章,如果有啥問題歡迎關註微信公眾號“程序員驛站”留言。好了,開始今天的正題,關於這塊的論述我按照按照下述結構進行。
電量消耗的全過程分析
手機設備會執行各種任務和各種復雜計算,如秀自拍圖片上傳朋友圈、秀直播等等,為了完成這些設備硬件會快速消耗手機電池電量。很明顯,任務處理的越復雜,電量就會消耗的越多和越快,一眨眼的功夫電量就消耗完了,這個時候用戶的手機頓時變成個累贅的磚頭了,用戶就會懷疑誰(哪個app)這麽耗電,然後把它卸了!
寫出耗電量低的應用的關鍵是要透徹理解它的全部過程。
在電子編程世界,這種硬件消耗電量 來執行任務的過程,叫做超時電流消耗,任何電子編程專業的人都會告訴你,你的設備的各項活動在相同時間內,消耗的電量是不同的。
比如,很多手機號稱待機好幾天,這個確實是真的,不過就是使用飛行模式放在家裏什麽都不幹,確實可以甚至可以堅持10多天。但是我們一旦使用它,比如使用蜂窩式無線數據交換(3G4G)、屏幕保持喚醒狀態等,手機電量就會很快被消耗掉。
作為開發者,我們很想知道我的應用執行的哪些任務消耗的電量是最多的?這個問題確實會很棘手。因為電量消耗的計算與統計是一件麻煩而且矛盾的事情,記錄電量消耗本身也是一個費電量的事情(所以很多設備都把這個監測電量的功能閹割掉了)。
唯一可行的方案是使用第三方監測電量的設備,這樣才能夠獲取到真實的電量消耗(因為第三方硬件監測的時候是用的自己的供電而不是用的手機的電量)。
耗電情況,例如:打開屏幕,所有要使用CPU/GPU工作的動作都會喚醒屏幕,都會消耗電量。這和應用程序喚醒設備還不一樣。比如使用叫醒鬧鐘(wake clock)、AlarmManager、JobSchedulerAPI。
手機哪些地方最耗電?
喚醒屏幕
當用戶點亮屏幕的時候,意味著系統的各組件要開始進行工作,界面也需要開始執行渲染。
待機狀態的電量消耗:
使用和喚醒屏幕後:
當設備從休眠狀態中,被應用程序喚醒時,可以看到在第一次喚醒時,出現一條電量使用高峰線。
CPU喚醒使用
CUP 喚醒時的高峰線:
接下來就是後續的一些執行的消耗了:
當工作完成後,設備會主動進行休眠,這非常重要,在不使用或者很少使用的情況下,長時間 保持屏幕喚醒會迅速消耗電池的電量。
蜂窩式無線
當設備通過無線網發送數據的時候,為了使用硬件,這裏會出現一個喚醒耗電高峰。接下來還 有一個高數值,這是發送數據包消耗的電量,然後接受數據包也會消耗大量電量 也看到一個峰值。
通常情況下,使用3G移動網絡傳輸數據,電量的消耗有三種狀態:
- Full power: 能量最高的狀態,移動網絡連接被激活,允許設備以最大的傳輸速率進
行操作。
- Low power: 一種中間狀態,對電量的消耗差不多是 Full power 狀態下的 50%。
- Standby: 最低的狀態,沒有數據連接需要傳輸,電量消耗最少。
Battery-Historian 電量分析工具的使用
要進行電量優化,我們首先得知道電都消耗到哪裏去了,我們可以通過 google 開源的 Battery-Historian 來進行分析。
工具開源地址: https://github.com/google/battery-historian
Battery History 工具安裝
根據 gitbub 上面介紹,Battery History
工具的安裝有兩種方式:
方式1: 通過安裝 Docker 環境來安裝。(這種方式很簡單,Docker 真心好用)
- 按照 Docker 網站上的說明安裝 Docker Community Edition。
- 使用以下命令運行 Battery Historian 鏡像:
docker --run -p port_number:9999 gcr.io/android-battery-historian:2.1 --port 9999
方式2 通過編譯 gitbub 上面的源碼來安裝。
- GO 環境安裝:具體可以參考 Mac os 安裝 golang 開發環境(https://www.jianshu.com/p/79bdd20c46cf)
- 安裝 git.
- 安裝 Python。僅支持 python2.7 (https://www.python.org/ )
- 安裝Java環境
下載 Battery Historian 源碼並且運行
輸入如下命令行 下載到GOPATH 配置目錄下。
go get -d -u github.com/google/battery-historian/...
進入到$GOPATH/src/github.com/google/battery-historian目錄下方
cd $GOPATH/src/github.com/google/battery-historian
運行 Battery Historian
1.執行命令:
go run setup.go
Compile Javascript files using the Closure compiler
2.接著在執行命令:
go run cmd/battery-historian/battery-historian.go [--port <default:9999>]
Run Historian on your machine (make sure $PATH contains $GOBIN)
3.登錄網址http://localhost:9999查看battery-historian是否運行。
到此Battery-historian的環境就整好了。
電量數據收集
Android 5.0 及以上的設備, 允許我們通過 adb 命令 dump 出電量使用統計信息。
1.因為電量統計數據是持續的, 會非常大, 統計待測試的 App 之前需要連上設備,因此需要reset(重置)電池數據收集。命令行執行:
$ adb shell dumpsys batterystats --resetBattery stats reset
2.斷開usb連接的測試設備, 操作要測試的App。
3.重新連接設備, 使用 adb 命令導出相關統計數據:
- Android 7.0 及以上執行如下命令:
adb bugreport > [path/]bugreport.zip
- Android 5.0/ 6.0執行如下命令:
adb bugreport > [path/]bugreport.txt
導出的統計數據存儲到 bugreport.zip(bugreport.txt), 借助 battery-historian 工具來圖形化 展示電池的消耗情況.
上傳 bugreport.zip(bugreport.txt)文件至 http://localhost:9999:
battery-historian電量分析結果:
分析指標
下圖是使用 adb 命令將采集的電量數據上傳至 Battery Historian 而得到電量的分析情況。(我們可以通過包名過濾具體應用的耗電情況)
各指標的含義
- 橫坐標: 橫坐標就是一個時間範圍,咱們的例子中統計的數據是以重置為起點,獲取 bugreport 內容時 刻為終點。我們一共采集了多長時間的數據;
- 縱坐標: 關鍵數據點說明如下。
數據項 | 說明 |
---|---|
battery_level | 電量,可以看出電量的變化 |
plugged | 充電狀態,這一欄顯示是否進行了充電,以及充電的時間範圍 |
screen | 屏幕是否點亮,這一點可以考慮到睡眠狀態和點亮狀態下電量的使用信息 |
top | 該欄顯示當前時刻哪個 app 處於最上層,就是當前手機運行的 app,用來判斷某個 app 對手機電量的影響,這樣也能判斷出該 app 的耗電量信息。該欄記錄了應用在某 一個時刻啟動,以及運行的時間,這對我們比對不同應用對性能的影響有很大的幫助 |
wake_lock | wake_lock 該屬性是記錄 wake_lock 模塊的工作時間。是否有停止的時候等 |
running | 界面的狀態,主要判斷是否處於 idle 的狀態。用來判斷無操作狀態下電量的消耗 |
Job | 後臺的工作,比如服務 service 的運行 |
data_conn | 數據連接方式的改變,上面的 edge 是說明采用的 gprs 的方式連接網絡的。此數據可 以看出手機是使用 2g,3g,4g 還是 wifi 進行數據交換的。這一欄可以看出不同的連 接方式對電量使用的影響 |
status | 電池狀態信息,有充電,放電,未充電,已充滿,未知等不同狀態 |
phone_signal_strength | 手機信號狀態的改變。 這一欄記錄手機信號的強弱變化圖,依次來判斷手機信號對電 量的影響 |
health | 電池健康狀態的信息,這個信息一定程度上反映了這塊電池使用了多長時間 |
plug | 充電方式,usb 或者插座,以及顯示連接的時間 |
Sync | 是否跟後臺同步 |
phone_in_call | 是否進行通話 |
gps | gps 是否開啟 |
如何進行電量優化?
了解手機關鍵耗電的地方及分析耗電的工具後。接下來就是我們的核心,如何來進行電量的優 化?首先我們先簡單總結匯總一下耗電的相關因素
- 屏幕亮暗相關
- 設備 awake,sleep 的切換,尤其是喚醒.
- CPU 運行相關
- 網絡
- 傳感器
我們都知道屏幕的渲染及 CPU 的運行是耗電的主要因素之一。所以當我們在做內存優化、渲染優化、計算優化的時候,就已然在做電量優化。所以在平時的開發中,我們要註意點滴性能 的優化積累,實際上當我們來做電量分析的時候,也是在找自己挖的坑。所以盡量有意識在項 目的開發過程中盡量少挖坑,這一點是我們在分析其他優化項首先要提到的一個點。
監聽手機充電狀態
我們可以通過下面的代碼來獲取手機的當前充電狀態:
得到充電狀態信息之後,我們可以有針對性的對部分代碼做優化。比如我們可以判斷只有當前 手機為 AC 充電狀態時 才去執行一些非常耗電的操作。可以通過下面的方法判斷手機當前的充 電狀態。
這裏我們就需要思考,根據具體的業務,考慮將一些不需要及時地和用戶交互的操作放到充電 的時候去做。比如:360 手機助手,當充上電的時候,才會自動清理手機垃圾,自動備份上傳圖片、聯系人 等到雲端,從而避免當用戶手機低電量時,任然繼續進行耗電操作。
屏幕喚醒
當 Android 設備空閑時,屏幕會變暗,然後關閉屏幕,最後會停止 CPU 的運行,這樣可以防 止電池電量掉的快。但有些時候我們需要改變 Android 系統默認的這種狀態:比如玩遊戲時我 們需要保持屏幕常亮,比如一些下載操作不需要屏幕常亮但需要 CPU 一直運行直到任務完成。
保持屏幕常亮比較好的方式是在 Activity 中使用 FLAG_KEEP_SCREEN_ON 的 Flag。
這個方法的好處是不像喚醒鎖(wake locks),需要一些特定的權限(permission)。並且能 正確管理不同 app 之間的切換,不用擔心無用資源的釋放問題。
另一個方式是在布局文件中使用 android:keepScreenOn 屬性:
android:keepScreenOn = “true”的作用和 FLAG_KEEP_SCREEN_ON 一樣,使用代碼的好 處是你允許你在需要的地方關閉屏幕。
註意:一般不需要人為的去掉 FLAG_KEEP_SCREEN_ON 的 flag,windowManager 會管理好程序進入 後臺回到前臺的的操作。如果確實需要手動清掉常亮的 flag,使用
所以這裏我們需要根據自己的 APP 實際情況,根據業務來控制好是否保持屏幕常量。比如 APP 需要支持視頻播放。那麽在播放的界面需要控制好不熄屏,當退出播放時,當然就沒有了 這個設置。
WakeLock
wake_lock 鎖主要是相對系統的休眠而言的,意思就是程序給 CPU 加了這個鎖那系統就不會 休眠了,這樣做的目的是為了全力配合我們程序的運行。有的情況如果不這麽做就會出現一些 問題。
需要使用 PowerManager 這個系統服務的喚醒鎖(wake locks)特征來保持 CPU 處於喚醒狀 態。喚醒鎖允許程序控制宿主設備的電量狀態,創建和持有喚醒鎖對電池的續航有較大的影 響,所以,除非是真的需要喚醒鎖完成盡可能短的時間在後臺完成的任務時才使用它。比如在 Acitivity 中就沒必要用了。如果需要關閉屏幕,使用上述的 FLAG_KEEP_SCREEN_ON。
只有一種合理的使用場景,使用後臺服務在屏幕關閉情況下 hold 住 CPU 完成一些工作,需要 使用喚醒鎖,如果不使用喚醒鎖來執行後臺服務,不能保證因 CPU 休眠未來的某個時刻任務 會停止,這不是我們想要的。
喚醒鎖可劃分並識別為四種用戶喚醒鎖:
標記值 | CPU | 屏幕 | 鍵盤 |
---|---|---|---|
PARTIAL_WAKE_LOCK | 開啟 | 關閉 | 關閉 |
SCREEN_DIM_WAKE_LOCK | 開啟 | 變暗 | 關閉 |
SCREEN_BRIGHT_WAKE_LOCK | 開啟 | 變亮 | 關閉 |
FULL_WAKE_LOCK | 開啟 | 變亮 | 變亮 |
註意:自 API 等級 17 開始,FULL_WAKE_LOCK 將被棄用。 應用應使用 FLAG_KEEP_SCREEN_ON。
1.添加喚醒鎖權限:
2.直接使用喚醒鎖:
註意:在使用該類的時候,必須保證 acquire 和 release 是成對出現的。不然當我們業務已經不需要時, 當 CPU 處於喚醒狀態,這個時候就會損耗多余的電量。
JobScheduler
自 Android 5.0 發布以來,JobScheduler 已成為執行後臺工作的很好的方式,其工作方式有 利於用戶在適當的時機執行正確的事情。應用可以在安排作業的同時允許系統基於內存、電源 和連接情況進行優化。JobSchedule 的宗旨就是把一些不是特別緊急的任務放到更合適的時機 批量處理。這樣做有兩個好處:
- 避免頻繁的喚醒硬件模塊,造成不必要的電量消耗。
- 避免在不合適的時間(例如低電量情況下、弱網絡或者移動網絡情況下的)執行過多的
任務消耗電量。
GPS
選擇合適的 Location Provider
Android 系統支持多個 Location Provider:
- GPS_PROVIDER: GPS 定位,利用 GPS 芯片通過衛星獲得自己的位置信息。定位精準度高,一般在 10 米左右, 耗電量大;但是在室內,GPS 定位基本沒用。
- NETWORK_PROVIDER: 網絡定位,利用手機基站和 WIFI 節點的地址來大致定位位置,這種定位方式取決於服務器,
即取決於將基站或 WIF 節點信息翻譯成位置信息的服務器的能力。 - PASSIVE_PROVIDER: 被動定位,就是用現成的,當其他應用使用定位更新了定位信息,系統會保存下來,該應用接 收到消息後直接讀取就可以了。
如果 App 只是需要一個粗略的定位那麽就不需要使用 GPS 進行定位,既耗費電量,定位的耗 時也久。
及時註銷定位監聽
在獲取到定位之後或者程序處於後臺時,註銷定位監聽,此時監聽 GPS 傳感器相當於執行 no- op(無操作指令),用戶不會有感知但是卻耗電。
多模塊使用定位盡量復
多個模塊使用定位,盡量復用上一次的結果,而不是都重新走定位的過程,節省電量損耗;例 如:在應用啟動的時候獲取一次定位,保存結果,之後再用到定位的地方都直接去取。
傳感器
使用傳感器,選擇合適的采樣率,越高的采樣率類型則越費電。
- SENSOR_DELAY_NOMAL (200000 微秒)
- SENSOR_DELAY_UI (60000 微秒)
- SENSOR_DELAY_GAME (20000 微秒)
- SENSOR_DELAY_FASTEST (0 微秒)
在後臺時註意及時註銷傳感器監聽
Doze and App Standby
最後提這一點,理論上不是電量優化,而是做電量優化要註意的一個坑。Doze and App Standby 是 Android 6.0 以後,提供了兩種省電延長電池壽命的功能。
具體可參考 google 官方介紹文檔。
如果大家有什麽好的意見或建議,歡迎關註我的公眾號“程序員驛站”進行留言,謝謝!
掃一掃 關註我的公眾號
如果你有好的文章需要和廣大網友分享,歡迎投稿,謝謝!
參考資料:https://github.com/google/battery-historian#wakelock-analysis
Android電量優化全解析