本篇文章是udacity上的Android Performace系列視頻-Battery篇的課程紀要。html
這個系列是視頻是Google和udacity合做推出的視頻,在Google的Android Performace Pattern系列視頻的基礎上加上了練習的章節,同時還有有趣的過場情節,很是不錯的視頻。java
課程地址:python
https://cn.udacity.com/course/android-performance--ud825android
- 電量消耗簡介
- Batter Historian(電池歷史)
- Track Battery Status using BatteryManager
- Wakelock and Battery Drain
- Network and Battery Drain
- JobSchedule API
- 總結
- 寫在最後
不一樣硬件的耗電量是不一樣的,同一個硬件在不一樣的狀態下的耗電量也是不一樣的。git
以Nexus 5爲例,若是開啓飛行模式不進行任何操做,電池大概能撐到一個月。咱們能夠把這個當成電池壽命的基準線。一旦活躍使用設備,就會消耗更多電量。這裏的「活躍」包括使用CPU、蜂窩無線數據交換、屏幕保持喚醒狀態等。github
首先咱們須要瞭解硬件的耗電特性和原理才能在寫應用時去規避耗電的操做。監測硬件方面的耗電量是個矛盾的問題,由於測量電量並記錄自己就是個耗電的過程,因此設備通常都沒有這個功能。檢測電量消耗最好的方式是再接入一個第三方硬件到安卓設備上,它負責進行記錄但不使用手機的電量。shell
下面咱們來看一下獲取到的一些數據信息:瀏覽器
當Nexus 5處於待機模式(standby mode),幾乎不消耗電量。緩存
一旦使用或點亮屏幕時。使用液晶屏,打開屏幕,全部的工做CPU、GPU都會喚醒屏幕,這都會消耗電量。因此會有一個耗電峯值。性能優化
若是設備是被應用喚醒的狀況下,好比使用wake lock、alarm manager或其餘job scheduler API。在第一次喚醒是,會有一次耗電高峯,接下來是具體執行的消耗。
值得注意的是,當工做完成後,設備會主動進行休眠。這一點很是重要,在不使用或不多使用的狀況下,長時間保持屏幕喚醒會迅速消耗電池電量。
另外一方面,蜂窩式無線是很是耗電的。當使用蜂窩網發送數據,首先會出現一個喚醒耗電高峯;接下來還有一個高數值,這個發送數據包消耗的電量;而後接收數據包也要消耗大量的電量;開啓無線模式這個過程很是耗電,因此完成執行工做後,它會在一小段時間內保持開啓模式,防止短期內還有數據包須要接收。關於蜂窩網絡能耗狀態的狀態機,能夠參考https://my.oschina.net/u/3026396/blog/821693。
以上的數據很是有用,能夠幫助咱們在寫程序時做爲參考。同時從Android L(5.0)開始,咱們可使用一個系統工具Battery Historian來分析並優化應用的耗電。
Battery Historian能夠幫助咱們收集數據,更好地瞭解應用程序對電量的使用。基本上是使用ADB來從手機上獲取數據,而後使用Battery Historian工具將這些數據轉化成HTML表格,在瀏覽器中打開閱讀。下面就讓咱們來看下Battery Historian的使用方法:
1. Battery Historian是一個獨立的Python開源腳本historian.py,咱們須要從GitHub上下載。https://github.com/google/battery-historian。
2. 把手機與電腦鏈接,打開USB調試。
3. 打開終端,分別執行
- adb kill-server:這一步很重要,由於當咱們開發時,在作電量記錄時,會打開不少可能形成衝突的東西。保險起見咱們要清除ADB。
- adb devices
- adb shell dumpsys batterystats --reset:重置電池數據收集,從空白狀態開始記錄。
- 將手機從電腦上拔出,使用手機電池進行咱們須要的操做。
- 操做完後從新將電腦與手機鏈接。
- adb shell dumpsys batterystats > xxx.txt:獲得整個設備的電量消耗信息。
- adb shell dumpsys batterystats > com.package.xxx > xxx.txt:獲得指定app的電量消耗信息。
- python historian.py xxx.txt > xxx_battery_stats.html:將信息轉化成可讀性強的html文件。
- 用瀏覽器打開html文件便可獲得相似於下圖的表格
須要注意的是,Battery Historian不會記錄某個活動具體的耗電量,而是記錄某個活動的好點時長和頻率。
關於Battery Historian更詳細的介紹,能夠參考:
咱們能夠經過下面的代碼來獲取手機的當前充電狀態:
// It is very easy to subscribe to changes to the battery state, but you can get the current // state by simply passing null in as your receiver. Nifty, isn't that? IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = this.registerReceiver(null, filter); int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC); if (acCharge) { Log.v(LOG_TAG,「The phone is charging!」); }在上面的例子演示瞭如何當即獲取到手機的充電狀態,獲得充電狀態信息以後,咱們能夠有針對性的對部分代碼作優化。好比咱們能夠判斷只有當前手機爲AC充電狀態時 纔去執行一些很是耗電的操做:
如下的代碼片斷,checkForPower()會返回當前設備是否在充電。代碼中會檢查三種充電狀態:經過USB充電、經過AC充電、經過無線充電。無線充電在API 17中才被引入,因此代碼中必須加入設備版本判斷。
/** * This method checks for power by comparing the current battery state against all possible * plugged in states. In this case, a device may be considered plugged in either by USB, AC, or * wireless charge. (Wireless charge was introduced in API Level 17.) */ private boolean checkForPower() { // It is very easy to subscribe to changes to the battery state, but you can get the current // state by simply passing null in as your receiver. Nifty, isn't that? IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = this.registerReceiver(null, filter); // There are currently three ways a device can be plugged in. We should check them all. int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean usbCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_USB); boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC); boolean wirelessCharge = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { wirelessCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS); } return (usbCharge || acCharge || wirelessCharge); }
高效的保留更多的電量與不斷促使用戶使用你的App會消耗電量,這是矛盾的選擇題。不過咱們可使用一些更好的辦法來平衡二者。
假設你的手機裏面裝了大量的社交類應用,即便手機處於待機狀態,也會常常被這些應用喚醒用來檢查同步新的數據信息。Android會不斷關閉各類硬件來延長手機的待機時間,首先屏幕會逐漸變暗直相當閉,而後CPU進入睡眠,這一切操做都是爲了節約寶貴的電量資源。可是即便在這種睡眠狀態下,大多數應用仍是會嘗試進行工做,他們將不斷的喚醒手機。一個最簡單的喚醒手機的方法是使用PowerManager.WakeLock的API來保持CPU工做(能夠設置不一樣參數決定屏幕和鍵盤的亮度)。這使得手機能夠被喚醒,執行工做,而後回到睡眠狀態。知道如何獲取WakeLock是簡單的,但是及時釋放WakeLock也是很是重要的,不恰當的使用WakeLock會致使嚴重錯誤。例如網絡請求的數據返回時間不肯定,致使原本只須要10s的事情一直等待了1個小時,這樣會使得電量白白浪費了。這也是爲什麼使用帶超時參數的wakelock.acquice()方法是很關鍵的。在出現了上述的意外狀況時,強制釋放喚醒鎖。
可是僅僅設置超時並不足夠解決問題,例如設置多長的超時比較合適?何時進行重試等等?解決上面的問題,正確的方式多是使用非精準定時器。
非精肯定時器容許咱們將工做轉移到未來的某一時刻。若是系統系統檢測到爲了保存電池壽命,它能夠早一點或晚一點執行,那麼它會等到那一時刻再執行。例如,若是另外一個進程喚醒了手機,咱們的應用可能會與那一進程同時喚醒,而不是按照以前設置的時間。
現實中,有不少狀況,可能進行密集電池工做更好。好比手機充電時或鏈接了wifi或已經被另外一個進程喚醒。若是咱們的工做能夠延遲到未來某一時刻,延遲到更理想的時刻,那麼會大大提升電池壽命。
咱們可使用Job Scheduler API來實現上述的目的,這個API在規劃將來工做、提高電池性能上發揮了很大做用。例如,非用戶操做能夠等到手機鏈接電源或接入wifi時再進行,或在某一時間內集中處理任務。
網絡行爲是最耗電的操做。網絡芯片開啓是會消耗大類電量,並且只要保持開啓狀態,就會持續耗電。
一般應用用到網絡有兩種場景:
- 必須當即執行的任務。這些任務是用戶行爲的結果 或者 是用戶須要更新界面的即時需求。好比刷新微博,這是用戶自主行爲,因此應用必須當即執行。
- 不須要當即執行的任務。好比上傳用戶數據、同步背景數據、更改圖片大小等。
因此第一種任務須要給用戶即刻反饋,第二種任務能夠延遲,能夠在電池方便時進行操做。應用的大部分網絡請求都屬於第二種。轉換網絡任務並提高效率可分爲2步,下來觀察一下下面的history historian結果:
mobile_radio一欄能夠看到不少窄的紅欄和空隙,這就表明移動網絡的活動和休眠切換的很頻繁。理想的狀況應該以下圖同樣有大段的紅欄和大段的空隙:
這表明咱們已經經過下降網絡鏈接次數,提升了性能,甚至沒有使用任何移動網絡鏈接。咱們能夠等手機接入wifi後,讓wifi硬件來處理,這樣節約了大量電量。
如今的問題是,用編寫代碼來分批處理(batch)、緩存(cache)並延遲(defer)這些網絡鏈接工做很是困難。Android 5.0引入的任務調度(JobScheduler)API能夠幫助咱們完成這個工做。
JobScheduler API可讓咱們進行任務的調度。能夠經過API指定job在手機插上電源、或鏈接網絡、或設備空閒的時候執行。
相關的類包括JobScheduler、JobInfo、JobService、JobParameters。
官網參考文檔:
JobInfo.Builder https://developer.android.google.cn/reference/android/app/job/JobInfo.Builder.html
JobScheduler
https://developer.android.google.cn/reference/android/app/job/JobScheduler.html
本文主要介紹了電量相關的性能優化。
首先介紹了Android系統耗電的原理,系統在平常活動中會觸發各個硬件的活動,不一樣的硬件設備的耗電量不一樣,同一個硬件設備在不一樣狀態下的耗電量也不一樣。好比屏幕、CPU、蜂窩網絡芯片等。
以後介紹了Android 5.0開始引入的電量信息採集工具 - Battery Historian。經過它咱們能夠分析在某個週期內系統的耗電狀況(也能夠針對某個應用作採集),從而讓咱們更容易地找出電量殺手,並進行優化。
官方給咱們介紹了3個電量優化的方向:
一、經過BatteryManger獲取當前手機的充電狀態:是否在充電、經過哪一種方式充電。根據這些信息,咱們的應用能夠採起不一樣的策略。
二、避免使用WakeLock喚醒系統,因爲某個應用阻塞或者多個應用不停喚醒,可能會致使系統長時間沒法獲得休眠。應該使用不精肯定時器,好比AlarmManager.setInexactXXX()或JobScheduler進行操做,系統會將相似行爲的任務合併在某一時間段內同時作,減小了系統喚醒的次數,從而節省的電量。
三、網絡操做是耗電大戶,有兩種類型的網絡操做,一種是要當即獲得反饋的,好比用戶發起的請求;一種是非當即的,好比後臺的定時請求。蜂窩網絡比wifi的耗電要多得多。在間隔時間不長的狀況下常常發起蜂窩網絡請求,會致使網絡設備不停地啓動休眠再啓動,這個過程會嚴重消耗電量。鑑於此,對於實時性不高的網絡請求,咱們須要減小請求的次數,將相同行爲的請求放在某一時間段內統一完成 或者 經過JobScheduler設置在鏈接wifi時再進行操做。
文中提到的一些電量優化方法也許實際編寫應用的過程當中用到的很少,除非要完成一些特定的需求才會用到,好比等到插上電源或wifi鏈接再進行操做這樣的方法。咱們須要理解的是文中經過分析原理找到優化方案的這種思想。固然若是咱們在編寫應用的時候須要實時記得咱們的行爲對電量的影響,在有文中相似場景的時候應用本文的方法或加以變通。