本文已同步發表到個人微信公衆號,掃一掃文章底部的二維碼或在微信搜索 「程序員驛站」便可關注,天天都會更新優質技術文章。python
電量優化一直是個老生常談的話題,關於這塊的文章已經有不少了,最近也在作這塊東西,因此結合本身的理解寫下這篇文章。好了開始咱們今天的正題,關於這塊的論述我按照下述結構進行。 android
手機設備會執行各類任務和各類複雜計算,如秀自拍圖片上傳朋友圈、秀直播等等,爲了完成這些設備硬件會快速消耗手機電池電量。很明顯,任務處理的越複雜,電量就會消耗的越多和越快,一眨眼的功夫電量就消耗完了,這個時候用戶的手機頓時變成個累贅的磚頭了,用戶就會懷疑誰(哪一個app)這麼耗電,而後把它卸了!git
寫出耗電量低的應用的關鍵是要透徹理解它的所有過程。程序員
在電子編程世界,這種硬件消耗電量 來執行任務的過程,叫作超時電流消耗,任何電子編程專業的人都會告訴你,你的設備的各項活動在相同時間內,消耗的電量是不一樣的。github
好比,不少手機號稱待機好幾天,這個確實是真的,不過就是使用飛行模式放在家裏什麼都不幹,確實能夠甚至能夠堅持10多天。可是咱們一旦使用它,好比使用蜂窩式無線數據交換(3G4G)、屏幕保持喚醒狀態等,手機電量就會很快被消耗掉。golang
做爲開發者,咱們很想知道個人應用執行的哪些任務消耗的電量是最多的?這個問題確實會很棘手。由於電量消耗的計算與統計是一件麻煩並且矛盾的事情,記錄電量消耗自己也是一個費電量的事情(因此不少設備都把這個監測電量的功能閹割掉了)。docker
惟一可行的方案是使用第三方監測電量的設備,這樣纔可以獲取到真實的電量消耗(由於第三方硬件監測的時候是用的本身的供電而不是用的手機的電量)。shell
耗電狀況,例如:打開屏幕,全部要使用CPU/GPU工做的動做都會喚醒屏幕,都會消耗電量。這和應用程序喚醒設備還不同。好比使用叫醒鬧鐘(wake clock)、AlarmManager、JobSchedulerAPI。
編程
當用戶點亮屏幕的時候,意味着系統的各組件要開始進行工做,界面也須要開始執行渲染。服務器
待機狀態的電量消耗:
使用和喚醒屏幕後:
當設備從休眠狀態中,被應用程序喚醒時,能夠看到在第一次喚醒時,出現一條電量使用高峯線。
CUP 喚醒時的高峯線:
接下來就是後續的一些執行的消耗了:
當工做完成後,設備會主動進行休眠,這很是重要,在不使用或者不多使用的狀況下,長時間 保持屏幕喚醒會迅速消耗電池的電量。
當設備經過無線網發送數據的時候,爲了使用硬件,這裏會出現一個喚醒耗電高峯。接下來還 有一個高數值,這是發送數據包消耗的電量,而後接受數據包也會消耗大量電量 也看到一個峯值。
一般狀況下,使用3G移動網絡傳輸數據,電量的消耗有三種狀態:
要進行電量優化,咱們首先得知道電都消耗到哪裏去了,咱們能夠經過 google 開源的 Battery-Historian 來進行分析。
工具開源地址: https://github.com/google/battery-historian
根據 gitbub 上面介紹,Battery History
工具的安裝有兩種方式:
方式1: 經過安裝 Docker 環境來安裝。(這種方式很簡單,Docker 真心好用)
docker --run -p port_number:9999 gcr.io/android-battery-historian:2.1 --port 9999
方式2 經過編譯 gitbub 上面的源碼來安裝。
輸入以下命令行 下載到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 命令導出相關統計數據:
adb bugreport > [path/]bugreport.zip
adb bugreport > [path/]bugreport.txt
導出的統計數據存儲到 bugreport.zip(bugreport.txt), 藉助 battery-historian 工具來圖形化 展現電池的消耗狀況.
上傳 bugreport.zip(bugreport.txt)文件至 http://localhost:9999:
battery-historian電量分析結果:
下圖是使用 adb 命令將採集的電量數據上傳至 Battery Historian 而獲得電量的分析狀況。(咱們能夠經過包名過濾具體應用的耗電狀況)
各指標的含義
數據項 | 說明 |
---|---|
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 是否開啓 |
瞭解手機關鍵耗電的地方及分析耗電的工具後。接下來就是咱們的核心,如何來進行電量的優 化?首先咱們先簡單總結匯總一下耗電的相關因素
咱們都知道屏幕的渲染及 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 須要支持視頻播放。那麼在播放的界面須要控制好不熄屏,當退出播放時,固然就沒有了 這個設置。
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 處於喚醒狀態,這個時候就會損耗多餘的電量。
自 Android 5.0 發佈以來,JobScheduler 已成爲執行後臺工做的很好的方式,其工做方式有 利於用戶在適當的時機執行正確的事情。應用能夠在安排做業的同時容許系統基於內存、電源 和鏈接狀況進行優化。JobSchedule 的宗旨就是把一些不是特別緊急的任務放到更合適的時機 批量處理。這樣作有兩個好處:
選擇合適的 Location Provider
Android 系統支持多個 Location Provider:
若是 App 只是須要一個粗略的定位那麼就不須要使用 GPS 進行定位,既耗費電量,定位的耗 時也久。
及時註銷定位監聽
在獲取到定位以後或者程序處於後臺時,註銷定位監聽,此時監聽 GPS 傳感器至關於執行 no- op(無操做指令),用戶不會有感知可是卻耗電。
多模塊使用定位儘可能復
多個模塊使用定位,儘可能複用上一次的結果,而不是都從新走定位的過程,節省電量損耗;例 如:在應用啓動的時候獲取一次定位,保存結果,以後再用到定位的地方都直接去取。
使用傳感器,選擇合適的採樣率,越高的採樣率類型則越費電。
在後臺時注意及時註銷傳感器監聽
最後提這一點,理論上不是電量優化,而是作電量優化要注意的一個坑。Doze and App Standby 是 Android 6.0 之後,提供了兩種省電延長電池壽命的功能。
具體可參考 google 官方介紹文檔。
參考資料:https://github.com/google/battery-historian#wakelock-analysis
關注個人技術公衆號"程序員驛站",天天都有優質技術文章推送,微信掃一掃下方二維碼便可關注: