Android優化(三)_延遲電池續航時間

性能優化總綱

大概會花一個月左右的時間出7-8個專題來分享一下在工做和學習中積累下來的Android性能優化經驗。html

但願你們會持續關注。python

如今是專題三:電池電量優化android

但這也僅僅是爲你們提供一些思路與較爲全面的總結,算不上什麼,但願有錯誤或問題在下面評論。算法

也歡迎點開個人掘金、簡書、CSDN主頁看看其餘文章。shell

最後完結之後會將思惟導圖與優化框架整理出來,請期待。性能優化

題記

電池雖小,地位卻很是重要。移動設備使用電池,作任何事情都要費電。而大多數狀況下,白天不多有機會給電池充電,哪怕你帶了電寶,也可能出現不夠用的狀況。服務器

而做爲開發者,若是你的程序被用戶發現耗電量過多很容易被卸載,不再用,是很是致命的,所以咱們要制定一系列解決方案,防止此類事情發生。網絡

本章帶領你們探討如何測量電池的使用量,以及便可以省電,又不影響用戶體驗的方法。app


1、電池

通常來講,充滿電的狀態能夠保證手機正常使用1-2天,除去屏幕和CPU所消耗的電量之外,設備使用多少電量嚴重以來全部應用都作了什麼,也就是取決於你的應用是如何設計和實現的。 通常有以下功能:框架

  • 執行代碼(顯而易見)
  • 數據傳輸(上傳和下載,使用WiFi,2G,3G,4G)
  • 定位
  • 傳感器
  • 渲染圖像
  • 喚醒任務 在學習如何最大限度的減小耗電量以前,咱們想要有辦法來測量。

測量電池用量

一、Battery Historian

Battery Historian是Android 5.0開始引入的新API。經過下面的指令,能夠獲得設備上的電量消耗信息:

$ adb shell dumpsys batterystats > xxx.txt  //獲得整個設備的電量消耗信息
$ adb shell dumpsys batterystats > com.package.name > xxx.txt //獲得指定app相關的電量消耗信息複製代碼

獲得了原始的電量消耗數據以後,咱們須要經過Google編寫的一個python腳本把數據信息轉換成可讀性更好的html文件:

$ python historian.py xxx.txt > xxx.html複製代碼

打開這個轉換事後的html文件,能夠看到相似TraceView生成的列表數據,這裏的數據信息量很大,這裏就不展開了。

3) Track Battery Status & Battery Manager

咱們能夠經過下面的代碼來獲取手機的當前充電狀態:

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,「Thephoneischarging!」);
}複製代碼

在上面的例子演示瞭如何當即獲取到手機的充電狀態,獲得充電狀態信息以後,咱們能夠有針對性的對部分代碼作優化。好比咱們能夠判斷只有當前手機爲AC充電狀態時 纔去執行一些很是耗電的操做。

private boolean checkForPower(){

IntentFilter filter=new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus=this.registerReceiver(null,filter);
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獲取電量算法。 通過查看源碼,咱們看到app計算電量的算法以下:

  • 在主Activity裏面 info.getBatteryStats() 就搞定了。 首先 load(),若是load失敗,走CPU時間計算,經過getAppListCpuTime這樣函數。 CPU的時間計算,有3個核心步驟:
  1. ActivityManager遍歷runningApp進程,獲取對應pid
  2. getAppProcessTime(pid)經過讀取/proc/pid/stat文件,拿取APP在CPU的運行時間。
  3. 從新爲BatterySipper附值:+time;
  • 獲取APP消耗 processAppUsage();也分三步走:
  1. 經過PowerProfile 獲取cpu的速度層次(speedsteps),方便後面使用
  2. 根據不一樣CPU的速度等級,計算cpu在某個速度下的電量,mA毫安
  3. mPowerProfile.getAveragePower(PowerProfile.POWERCPUACTIVE,p)

不少地方都用到這個API獲取power。 那它究竟作了些什麼呢? 查看系統源碼能夠知道:

實際上這句話是獲取1個叫PowerMap的數據結果,得到電量。而PoweMap的賦值,是來源於com.android.internal.R.xml.powerprofile 的文件。 關於該文件的獲取 android-版本號/core/res/res/xml/powerprofile.xml

計算各類耗電量的詳細算法是: 來自深刻淺出Android App耗電量統計 總結App耗電量計算公式:(

Uid_Power(App耗電量,單位:mAh) = Uid_Power1 + Uid_Power2 + Uid_Power3 + Uid_Power4 + Uid_Power5

Uid_Power1 = (Process1_Power + … + ProcessN_Power);

Process_Power = (CPUSpeed_Time * POWER_CPU_ACTIVE);

Uid_Power2 = PartialWakeLock_Time * POWER_CPU_WAKE

Uid_Power3 = ( tcpBytesReceived + tcpBytesSent ) * averageCostPerByte

Uid_Power4 = wifiRunningTimeMs * POWER_WIFI_ON

Uid_Power5 = (Sensor1_Power + … + SensorN_Power)

Sensor_Power = Sensor_Time * Power_Sensor

2、禁用電池廣播

系統除了定義了ACTION_BATTERY_CHANGED包含了電池信息,還定義了應用可使用的4個廣播Intent:

  • ACTION_BATTERY_LOW
  • ACTION_BATTERY_OKAY
  • ACTION_POWER_CONNECRED
  • ACTION_POWER_DISCONNECTED

當咱們在一個廣播接收器接收系統發送的這四個廣播時,只要有一個發生,應用就會啓動。這樣有一個嚴重的缺陷,若是你在前臺運行時,是沒有問題的,可是若是在後臺時,還出現Toast消息(非系統提醒),就有可能干擾其餘應用,損害用戶體驗

因此咱們有一個很好的解決:

  • 只有當應用在前臺運行時才能夠啓用廣播
  • 步驟: 一、廣播接收器默認必須是禁用 二、廣播接收器必須在onResume()中啓用,在onPause()被禁用

3、控制網絡

如今基本全部的應用都必須在設備和服務器之間傳遞數據,就像獲取電池狀態同樣,應用須要獲取設備商的網絡連接信息。ConnectivityManager類提供了API。供應用調用以此訪問網絡信息。 Android設備一般由多個數據鏈接:

  • Bluetooth
  • Ethernet
  • Wi_Fi
  • WiMax
  • 移動網絡(EDGE、UMTS、LTE)

爲了最大限度延長電池的使用時間,咱們須要直到以下事情:

  • 後臺數據設置
  • 數據傳輸頻度

後臺數據

能夠經過下面這個方法來獲取後臺數據的設置,不過在4.0之後,這個方法始終返回爲true,當強行不容許時,網絡會斷開。

ConnectivityManager的getBackgroundDataSetting()複製代碼

數據傳輸

傳輸速率的差別很是大,從小於每秒100Kb的GPRS數據鏈接到每秒幾Mb的LTE或Wifi都有。除了鏈接類型,NetWorkInfo類還指定了鏈接的子類型,例如:

  • NETWORK_TYPE_GPRS(API 1)

  • NETWORK_TYPE_LTE(API 11)

  • NETWORK_TYPE_HSPAP(API 13)

若是建立和部署了新技術,也會增長新的子類型。留意一下每一個SDK版本的改動。

人們都習慣更快的鏈接,即便WiFi芯片耗電量比較多,但Wifi的速率和免費可讓數據在最短期最小成本完成傳輸,從而下降電池消耗。

若是能控制數據的傳輸類型,就能夠現壓縮數據,再傳輸到設備上。雖然解壓縮數據耗費CPU,也多用了些電量,但傳輸速度大大加快,數據通信設備能夠很快關閉,從而延長了電池壽命。一般咱們這麼作:

  • 使用GZIP壓縮文本數據,使用GZIPInputStream類訪問數據;
  • 使用匹配設備分辨率的資源(好比:沒必要爲320480的屏幕下載19201080的圖片)

4、定位

如今不少的App都會作一件事:獲取你的定位信息。一些用戶可能願意犧牲電池壽命來頻繁的更新位置,而其餘人定遠縣誌更新次數,以確保設備點亮不會很快消耗完,因此須要提供不一樣的昔陽縣來知足用戶需求。

一、註銷監聽器 仍是和處理廣播那樣,在onPause()中調用removeUpdates()能夠註銷監聽器。

二、而且能夠用requestLocationUpdates()調整更新頻率。選擇合適的時間間隔和最小間隔距離能夠適應不一樣的場景。

三、經過選擇不一樣的位置服務來控制,以下:

  • GPS定位
  • WIFI定位
  • 基站定位
  • AGPS定位

這四種定位的概念想必大多數都知道了,不知道的戳這裏


5、傳感器

傳感器是個頗有意思的東西,與定位服務有點相似:應用向特定的傳感器註冊監聽器,得到更新通知。

也是能夠經過下降通知頻率來省點,因爲,每一個設備不一樣,應用能夠測量這4種延遲通知的頻率,選擇兼顧用戶體驗和省點的那一個。另外一種策略是,當發現值不變化時,使用NORMAL或UI延遲,當發現有忽然變化的時候,且話到GAME或FASTEST延遲。以下:

  • SENSOR_DELAY_NORMAL
  • SENSOR_DELAY_UI
  • SENSOR_DELAY_GAME
  • SENSOR_DELAY_FASTEST

6、圖形

應用花費了不少時間在屏幕上畫東西,不管是使用GPU渲染的3D遊戲仍是使用CPU的日曆程序,都想只以最少的代價賴在屏幕上展現指望的結果,以延長電池壽命。

如前所述,CPU非全速時使用的電量相對少一些。現代的CPU使用動態調頻喝點呀來節省電力和減小發熱量。這兩種技術一般一塊兒使用,成爲DVFS技術(動態電壓和頻率調整),Linux的內核、Android以及現代處理器都支持這種技術。

雖然你不能直接控制電壓和頻率,或將內部組建斷電,但能夠控制應用渲染的方式。流暢的幀速率仍是要達到的。雖然在Android上幀率有上線(每秒60幀),但優化渲染仍是有效果的。除了可能下降能耗,你可能夠爲後臺運行的應用留出更多的空間,提供了更好的總體用戶體驗。

例如:好比咱們手機裏的壁紙,能夠調用onVisibilityChanged()方法。事實上,壁紙能夠是不可見的。但很容易忘記,持續繪製壁紙會消耗不少的電量。


7、喚醒

有時候你得應用可能出於某種緣由,需不時的被喚醒,去執行一些操做。

可是不多應用真正須要到提醒時間去強行喚醒設備。固然鬧鐘這類程序會須要這種功能,但大可能是等到用戶主動喚醒才工做。

多數狀況下,應用須要將將來某一刻安排提醒到時,但對時間要求並非很嚴格。爲此Android定義了AlarmManager.setInexactRepeating(),它的參數和其「兄弟」setPepeating()基本相同,這種題型更節能,系統也避免了出現沒必要要的喚醒,Android定義了5個提醒間隔:

  • INTERVAL_FIFTEEN_MINUTES
  • INTERVAL_HALF_HOUR
  • INTERVAL_HOUR
  • INTERVAL_HALF_DAY
  • INTERVAL_DAY

最好的結果就是全部應用都使用這種提醒,而不用精確的觸發提醒。爲了儘量的節電,應用還可讓用戶配置提醒的調度,由於有人發現較長時間間隔並不會對用戶體驗有很差的影響。


8、WakeLock

有些時候,一些應用即便長時間不和設備交互,也要阻止進入休眠狀態,來保持良好的用戶體驗,就好比在看視頻的時候,這種狀況下,CPU須要作視頻解碼,同時屏幕保持開啓,讓用戶可以觀看。此外,視頻播放時屏幕不能變暗。

Android爲這種狀況設計了WakeLoac類

private void runInWakeLoac(Runnable runnable,int flags){
    PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLoac wl = pm.newWakeLock(flag,"My WakeLock");
    wl.acquire();
    runnable.run();
    wl.release();
}複製代碼

有一點須要注意:須要WAKE_LOCK權限。

系統的行爲取決於WakeLock對象建立時傳入的flags:

  • PARTIAL_WAKE_LOCK(CPU開)
  • SCREEN_DIM_WAKE_LOCK(CPU開、暗色顯示)
  • SCREEN_BRIGHT_WAKE_LOCK(CPU開、明亮顯示)
  • FULL_WAKE_LOCK(CPU開、明亮顯示、鍵盤開) 這些標記能夠結合使用
  • ACQUIRE_CAUSES_WAKEUP(打開屏幕和鍵盤)
  • ON_AFTER_RELEASE(WakeLock是放後繼續保持屏幕和鍵盤開啓片刻) 特別重要的一點:必定要釋放WakeLock,在退出和暫停的時候,不然可能一直顯示,電量很快耗光。

預防問題

防止出現特殊的問題,建議使用帶超時的WakeLoac.acquire()版本,它會在超時後自動釋放。 另外:能夠用setKeepScreenOn()方法控制是否要保持屏幕,只要可見的View指定了要保持屏幕,屏幕就會一直保留。

9、總結

用戶不會注意到應用是否延長了電池壽命。可是若是不作任何處理,那就有可能被注意到了。由於單個應用耗電過多,用戶幾天仍是能夠感覺出來的,用戶到時候就會卸載用用,因此要對電量使用作一些優化處理,而且給用戶配置選項的自由,來應對用戶產生的各類需求。

相關文章
相關標籤/搜索