App優化之電池省著用
引言
電量使用優化, 基本上是我們最不怎么關注的一項優化. 可能很多公司連QA/Tester也不會關注測試App電量的使用. 一般來說開發和測試的測試設備也一直是連著USB處于充電狀態的, 感官上也體會不到電量的損耗.
然而, 對于用戶來說, 實際上App的電量損耗也是用戶體驗的一個方面. 特別是當今人們對移動設備的依賴度越來越高, 電量也是用戶特別關注的.
今天我們就來聊聊Android App的電量優化.
1, 分析電量的使用情況
老套路, 上來還是先介紹下我們使用什么工具來做電量分析.
1.1 Batterystats & bugreport
Android 5.0及以上的設備, 允許我們通過adb命令dump出電量使用統計信息.
1, 因為電量統計數據是持續的, 會非常大, 統計我們的待測試App之前先reset下, 連上設備, 命令行執行:
$ adb shell dumpsys batterystats --reset
Battery stats reset.
2, 斷開測試設備, 操作我們的待測試App.
3, 重新連接設備, 使用adb命令導出相關統計數據:
// 此命令持續記錄輸出, 想要停止記錄時按Ctrl+C退出.
$ adb bugreport > bugreport.txt
導出的統計數據存儲到bugreport.txt, 此時我們可以借助如下工具來圖形化展示電池的消耗情況.
注意, 官方SDK文檔 導出文件方式為:
adb shell dumpsys batterystats > batterystats.txt
使用python historian.py batterystats.txt > batterystats.html查看數據
是battery-historian老版本的使用方式. 目前Battery Historian已更新2.0版本, 推薦使用bugreport方式導出數據分析, 可以看到更多信息.
1.2 Battery Historian
Google提供了一個開源的電池歷史數據分析工具 -- Battery Historian .
1.2.1 安裝
按照 Battery Historian 在github上的readme, 一步步安裝即可.
需要注意的是, Battery Historian是Go語言的, 安裝Go的時候需要配置其bin的環境變量.
Python環境需要是2.7的(3.x不行), 建議使用pyenv管理本地的python環境.
另外, 因為Battery Historian是一個網頁版工具, 涉及一些JS引用, 有時需國內或許不能訪問.
安裝完成后, 執行:
cd $GOPATH/src/github.com/google/battery-historian
go run cmd/battery-historian/battery-historian.go [--port <default:9999>]
程序運行在 http://localhost:9999 , 如下:
battery historian running web
1.2.2 界面
導入我們在第一步通過adb bugreport生成的bugreport.txt文件:
battery historian
2, 主要的耗電因素
battery usage
從手機的電池詳情統計可以簡單看出, 手機中最耗電的模塊肯定是屏幕了, 接著就是網絡相關, 另外可能的耗電大戶還有GPS芯片, Camera等.
對于一個App, 對應因素主要有:
2.1 網絡請求
我們可能會有發現:
- 測試用的手機充滿電放了一個十一假期還有電, 是因為測試手機沒有上SIM卡.
- 飛行模式下的手機滅屏下, 可能可以放一個月都還有電.
這是因為:
- 手機的通過內置的射頻模塊和基站幾乎, 從而鏈接上網的, 而這個射頻模塊(radio)是非常耗電的.
- 為了控制這個射頻模塊的耗電, 硬件驅動及Android RIL層做了很多處理. 例如可以單獨關閉radio(飛行模式), 間歇性假休眠radio(有數據發生時才上電, 保持一個頻率的與基站交互)等等.
現如今App都是移動互聯網App, 不可避免的會有大量的網絡請求, 會導致radio一直處于活躍狀態, 從而耗電量增加.
2.2 WakeLock
Android系統本身為了優化電量的使用, 會在沒有操作時進入休眠狀態, 來節省電量. 當然, 為了便于開發(很多應用不可避免的希望在滅屏后還能運行一些事兒, 或是要保持屏幕一直亮著--比如播放視頻), Android提供了一個PowerManager.WakeLock的東西.
我們可以用WakeLock來保持CPU運行, 或是防止屏幕變暗/關閉, 讓手機可以在用戶不操作時依然可以做一些事兒. 然而, 獲取WakeLock很容易, 釋放不好就會成為難題, 消耗電量.
例如我們獲取了一個WakeLock來保持CPU運轉, 做一個復雜運算并將數據上傳到后臺服務器, 然后釋放該WakeLock. 然而這個過程可能并不像我們想象的那么快, 可能因為比如服務器掛掉, 計算出了異常等等WakeLock沒有釋放. 問題就來了, CPU會一直得不到休眠, 而大大增加耗電.
另外, WakeLock還有android:keepScreenOn屬性, 還可以讓屏幕常量, 這可是耗電大戶.
2.3 GPS
應用中經常會用到定位服務, Android提供了Network定位和GPS定位. 相對來說, GPS會精確得多, 對于一些諸如跑步, 導航類的應用基本會使用GPS定位. 然而, GPS定位也會消耗大量的電量.
3, 盡可能減少App的電量消耗的建議
了解了上述的主要的耗電因素, 還有一些程序的耗電問題, 我們通過Battery Historian也可以分析.
針對這些耗電情況, 給出如下優化建議:
3.1 優化網絡請求
這個會在網絡優化那篇中細聊, 在此略過.
3.2 謹慎使用WakeLock
- WakeLock獲取釋放成對出現.
- 使用超時WakeLock, 以防出異常導致沒有釋放.
// Acquires the wake lock with a timeout.
acquire(long timeout)
3.3 監聽手機充電狀態
BatteryManager會發送一個包含充電狀態的持續廣播, 我們可以通過此廣播獲取充電狀態和電量詳情:
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
注意: 因為這是一個持續廣播, 我們無需寫receiver, 可以直接通過intent獲取相關數據.
例如, 如果設備正在充電:
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
另外我們也可以監聽充電狀態的變化, 只要設備連接或斷開電源, BatteryManager就會廣播相應的操作, 我們可以注冊receiver來監聽:
<receiver android:name=".PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
監聽電池狀態, 可以讓我們將一些操作放在充電或是電量足夠的情況下進行, 以提升用戶體驗. 例如用戶數據同步, Log上傳等.
3.4 Doze and App Standby
Android 6.0提供了兩個用來節省電量的技術Doze和App Standby.
-
Doze
瞌睡. 如果設備閑置了一段較長時間, Doze技術將通過延遲后臺網絡活動, CPU運行等來減少電量損耗.
-
App Standy
應用待機. 不是最近得到過用戶"寵幸"的App, App Standy將延緩這個應用的后臺網絡活動.
因為所有Android 6.0及以上的設備上, Doze and App Standby都會運行. 可能會影響你的App的運行, 具體的適配請參考 官方文檔 .
3.5 關于定位
-
定位中使用GPS, 請記得及時關閉
// Remove the listener you previously added
locationManager.removeUpdates(locationListener);
-
減少更新頻率
-
根據實際情況選擇GPS或網絡或兩者. 只使用一個會降低電量損耗.
來自:http://www.jianshu.com/p/c55ef05c0047