Android優(yōu)化(三)_延遲電池續(xù)航時間

性能優(yōu)化總綱

大概會花一個月左右的時間出7-8個專題來分享一下在工作和學習中積累下來的Android性能優(yōu)化經(jīng)驗滋早。

希望大家會持續(xù)關注。

現(xiàn)在是專題三:電池電量優(yōu)化
但這也僅僅是為大家提供一些思路與較為全面的總結憔辫,算不上什么,希望有錯誤或問題在下面評論。

最后完結以后會將思維導圖與優(yōu)化框架整理出來苇瓣,請期待奇徒。

題記

電池雖小雏亚,地位卻非常重要。移動設備使用電池摩钙,做任何事情都要費電罢低。而大多數(shù)情況下,白天很少有機會給電池充電胖笛,哪怕你帶了電寶网持,也可能出現(xiàn)不夠用的情況。而作為開發(fā)者长踊,如果你的程序被用戶發(fā)現(xiàn)耗電量過多很容易被卸載功舀,再也不用,是非常致命的身弊,因此我們要制定一系列解決方案辟汰,防止此類事情發(fā)生。本章帶領大家探討如何測量電池的使用量阱佛,以及即可以省電帖汞,又不影響用戶體驗的方法。


一凑术、電池

一般來說翩蘸,充滿電的狀態(tài)可以保證手機正常使用1-2天,除去屏幕和CPU所消耗的電量以外淮逊,設備使用多少電量嚴重以來所有應用都做了什么催首,也就是取決于你的應用是如何設計和實現(xiàn)的。
一般有如下功能:

  • 執(zhí)行代碼(顯而易見)
  • 數(shù)據(jù)傳輸(上傳和下載壮莹,使用WiFi翅帜,2G,3G,4G)
  • 定位
  • 傳感器
  • 渲染圖像
  • 喚醒任務在學習如何最大限度的減少耗電量之前,我們想要有辦法來測量命满。

測量電池用量####

1涝滴、Battery Historian
Battery Historian是Android 5.0開始引入的新API。通過下面的指令,可以得到設備上的電量消耗信息:

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

得到了原始的電量消耗數(shù)據(jù)之后歼疮,我們需要通過Google編寫的一個python腳本把數(shù)據(jù)信息轉換成可讀性更好的html文件:

$ python historian.py xxx.txt > xxx.html

打開這個轉換過后的html文件杂抽,可以看到類似TraceView生成的列表數(shù)據(jù),這里的數(shù)據(jù)信息量很大韩脏,這里就不展開了缩麸。

  1. Track Battery Status & Battery Manager
    我們可以通過下面的代碼來獲取手機的當前充電狀態(tài):
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!”);
}

在上面的例子演示了如何立即獲取到手機的充電狀態(tài),得到充電狀態(tài)信息之后赡矢,我們可以有針對性的對部分代碼做優(yōu)化杭朱。比如我們可以判斷只有當前手機為AC充電狀態(tài)時 才去執(zhí)行一些非常耗電的操作。

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);
}

2吹散、另一種方法得到耗電量

APP獲取電量算法弧械。經(jīng)過查看源碼,我們看到app計算電量的算法如下:

  • 在主Activity里面 info.getBatteryStats() 就搞定了空民。 首先 load()刃唐,如果load失敗,走CPU時間計算界轩,通過getAppListCpuTime這樣函數(shù)画饥。 CPU的時間計算,有3個核心步驟:

    1. ActivityManager遍歷runningApp進程浊猾,獲取對應pid
    2. getAppProcessTime(pid)通過讀取/proc/pid/stat文件抖甘,拿取APP在CPU的運行時間。
    3. 重新為BatterySipper附值:+time与殃;
  • 獲取APP消耗 processAppUsage();也分三步走:

  1. 通過PowerProfile 獲取cpu的速度層次(speedsteps)单山,方便后面使用
  2. 根據(jù)不同CPU的速度等級,計算cpu在某個速度下的電量幅疼,mA毫安
  3. mPowerProfile.getAveragePower(PowerProfile.POWERCPUACTIVE,p)

很多地方都用到這個API獲取power。那它究竟做了些什么呢?查看系統(tǒng)源碼可以知道:

實際上這句話是獲取1個叫PowerMap的數(shù)據(jù)結果昼接,獲得電量爽篷。
而PoweMap的賦值,是來源于com.android.internal.R.xml.powerprofile 的文件慢睡。

關于該文件的獲取 android-版本號/core/res/res/xml/powerprofile.xml

計算各種耗電量的詳細算法是
來自深入淺出Android App耗電量統(tǒng)計

總結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

二漂辐、禁用電池廣播

系統(tǒng)除了定義了ACTION_BATTERY_CHANGED包含了電池信息泪喊,還定義了應用可以使用的4個廣播Intent:

  • ACTION_BATTERY_LOW
  • ACTION_BATTERY_OKAY
  • ACTION_POWER_CONNECRED
  • ACTION_POWER_DISCONNECTED

當我們在一個廣播接收器接收系統(tǒng)發(fā)送的這四個廣播時,只要有一個發(fā)生髓涯,應用就會啟動袒啼。這樣有一個嚴重的缺陷,如果你在前臺運行時,是沒有問題的蚓再,但是如果在后臺時滑肉,還出現(xiàn)Toast消息(非系統(tǒng)提醒),就有可能干擾其他應用摘仅,損害用戶體驗靶庙。

所以我們有一個很好的解決:

  • 只有當應用在前臺運行時才可以啟用廣播
  • 步驟:
    1、廣播接收器默認必須是禁用
    2娃属、廣播接收器必須在onResume()中啟用六荒,在onPause()被禁用

三、控制網(wǎng)絡

現(xiàn)在基本所有的應用都必須在設備和服務器之間傳遞數(shù)據(jù)矾端,就像獲取電池狀態(tài)一樣掏击,應用需要獲取設備商的網(wǎng)絡鏈接信息。
ConnectivityManager類提供了API须床。供應用調用以此訪問網(wǎng)絡信息铐料。
Android設備通常由多個數(shù)據(jù)連接:

  • Bluetooth
  • Ethernet
  • Wi_Fi
  • WiMax
  • 移動網(wǎng)絡(EDGE、UMTS豺旬、LTE)

為了最大限度延長電池的使用時間钠惩,我們需要直到如下事情:

  • 后臺數(shù)據(jù)設置
  • 數(shù)據(jù)傳輸頻度

后臺數(shù)據(jù)

可以通過下面這個方法來獲取后臺數(shù)據(jù)的設置,
不過在4.0以后族阅,這個方法始終返回為true篓跛,當強行不允許時,網(wǎng)絡會斷開坦刀。

 ConnectivityManager的getBackgroundDataSetting()

數(shù)據(jù)傳輸傳輸速率的差異非常大愧沟,從小于每秒100Kb的GPRS數(shù)據(jù)連接到每秒幾Mb的LTE或Wifi都有。除了連接類型鲤遥,NetWorkInfo類還指定了連接的子類型沐寺,例如:

  • NETWORK_TYPE_GPRS(API 1)
  • NETWORK_TYPE_LTE(API 11)
  • NETWORK_TYPE_HSPAP(API 13)
    如果創(chuàng)建和部署了新技術,也會增加新的子類型盖奈。留意一下每個SDK版本的改動混坞。人們都習慣更快的連接,即使WiFi芯片耗電量比較多钢坦,但Wifi的速率和免費可以讓數(shù)據(jù)在最短時間最小成本完成傳輸究孕,從而降低電池消耗。

如果能控制數(shù)據(jù)的傳輸類型爹凹,就可以現(xiàn)壓縮數(shù)據(jù)厨诸,再傳輸?shù)皆O備上。雖然解壓縮數(shù)據(jù)耗費CPU禾酱,也多用了些電量微酬,但傳輸速度大大加快绘趋,數(shù)據(jù)通訊設備可以很快關閉,從而延長了電池壽命得封。通常我們這么做:

  • 使用GZIP壓縮文本數(shù)據(jù)埋心,使用GZIPInputStream類訪問數(shù)據(jù);

  • 使用匹配設備分辨率的資源(比如:不必為320480的屏幕下載19201080的圖片)


四忙上、定位

現(xiàn)在很多的App都會做一件事:獲取你的定位信息拷呆。一些用戶可能愿意犧牲電池壽命來頻繁的更新位置,而其他人定遠縣志更新次數(shù)疫粥,以確保設備點亮不會很快消耗完茬斧,所以需要提供不同的昔陽縣來滿足用戶需求。

1梗逮、注銷監(jiān)聽器還是和處理廣播那樣项秉,在onPause()中調用removeUpdates()可以注銷監(jiān)聽器。
2慷彤、并且可以用requestLocationUpdates()調整更新頻率娄蔼。選擇合適的時間間隔和最小間隔距離可以適應不同的場景。
3底哗、通過選擇不同的位置服務來控制岁诉,如下:

  • GPS定位
  • WIFI定位
  • 基站定位
  • AGPS定位
    這四種定位的概念想必大多數(shù)都知道了,不知道的戳這里

五跋选、傳感器

傳感器是個很有意思的東西涕癣,與定位服務有點類似:應用向特定的傳感器注冊監(jiān)聽器,獲得更新通知前标。
也是可以通過降低通知頻率來省點坠韩,由于,每個設備不同炼列,應用可以測量這4種延遲通知的頻率只搁,選擇兼顧用戶體驗和省點的那一個。另一種策略是俭尖,當發(fā)現(xiàn)值不變化時须蜗,使用NORMAL或UI延遲,當發(fā)現(xiàn)有突然變化的時候目溉,且話到GAME或FASTEST延遲。
如下:

  • SENSOR_DELAY_NORMAL
  • SENSOR_DELAY_UI
  • SENSOR_DELAY_GAME
  • SENSOR_DELAY_FASTEST

六菱农、圖形

應用花費了很多時間在屏幕上畫東西缭付,無論是使用GPU渲染的3D游戲還是使用CPU的日歷程序,都想只以最少的代價賴在屏幕上展示期望的結果循未,以延長電池壽命陷猫。

如前所述秫舌,CPU非全速時使用的電量相對少一些。現(xiàn)代的CPU使用動態(tài)調頻喝點呀來節(jié)省電力和減少發(fā)熱量绣檬。這兩種技術通常一起使用足陨,成為DVFS技術(動態(tài)電壓和頻率調整),Linux的內核娇未、Android以及現(xiàn)代處理器都支持這種技術墨缘。

雖然你不能直接控制電壓和頻率,或將內部組建斷電零抬,但可以控制應用渲染的方式镊讼。流暢的幀速率還是要達到的。雖然在Android上幀率有上線(每秒60幀)平夜,但優(yōu)化渲染還是有效果的蝶棋。除了可能降低能耗,你可可以為后臺運行的應用留出更多的空間忽妒,提供了更好的整體用戶體驗玩裙。

例如:比如我們手機里的壁紙,可以調用onVisibilityChanged()方法段直。事實上吃溅,壁紙可以是不可見的。但很容易忘記坷牛,持續(xù)繪制壁紙會消耗很多的電量罕偎。


七、喚醒

有時候你得應用可能出于某種原因京闰,需不時的被喚醒颜及,去執(zhí)行一些操作。>
但是很少應用真正需要到提醒時間去強行喚醒設備蹂楣。當然鬧鐘這類程序會需要這種功能俏站,但大多是等到用戶主動喚醒才工作。

多數(shù)情況下痊土,應用需要將未來某一刻安排提醒到時肄扎,但對時間要求并不是很嚴格。為此Android定義了AlarmManager.setInexactRepeating(),它的參數(shù)和其“兄弟”setPepeating()基本相同赁酝,這種題型更節(jié)能犯祠,系統(tǒng)也避免了出現(xiàn)不必要的喚醒,Android定義了5個提醒間隔:

  • INTERVAL_FIFTEEN_MINUTES
  • INTERVAL_HALF_HOUR
  • INTERVAL_HOUR
  • INTERVAL_HALF_DAY
  • INTERVAL_DAY

最好的結果就是所有應用都使用這種提醒酌呆,而不用精確的觸發(fā)提醒衡载。為了盡可能的節(jié)電,應用還可以讓用戶配置提醒的調度隙袁,因為有人發(fā)現(xiàn)較長時間間隔并不會對用戶體驗有不好的影響痰娱。


八弃榨、WakeLock

有些時候,一些應用即使長時間不和設備交互梨睁,也要阻止進入休眠狀態(tài)鲸睛,來保持良好的用戶體驗,就比如在看視頻的時候坡贺,這種情況下官辈,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權限风瘦。
系統(tǒng)的行為取決于WakeLock對象創(chuàng)建時傳入的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是放后繼續(xù)保持屏幕和鍵盤開啟片刻)

特別重要的一點:一定要釋放WakeLock官帘,在退出和暫停的時候瞬雹,否則可能一直顯示,電量很快耗光刽虹。

預防問題

防止出現(xiàn)特殊的問題酗捌,建議使用帶超時的WakeLoac.acquire()版本,它會在超時后自動釋放涌哲。另外:可以用setKeepScreenOn()方法控制是否要保持屏幕胖缤,只要可見的View指定了要保持屏幕,屏幕就會一直保留阀圾。


九哪廓、總結

用戶不會注意到應用是否延長了電池壽命。但是如果不做任何處理初烘,那就有可能被注意到了涡真。因為單個應用耗電過多,用戶幾天還是可以感受出來的肾筐,用戶到時候就會卸載用用哆料,所以要對電量使用做一些優(yōu)化處理,并且給用戶配置選項的自由吗铐,來應對用戶產(chǎn)生的各種需求剧劝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抓歼,隨后出現(xiàn)的幾起案子讥此,更是在濱河造成了極大的恐慌,老刑警劉巖谣妻,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萄喳,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹋半,警方通過查閱死者的電腦和手機他巨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來减江,“玉大人染突,你說我怎么就攤上這事”沧疲” “怎么了份企?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長巡莹。 經(jīng)常有香客問我司志,道長,這世上最難降的妖魔是什么降宅? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任骂远,我火速辦了婚禮,結果婚禮上腰根,老公的妹妹穿的比我還像新娘激才。我一直安慰自己,他們只是感情好额嘿,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布瘸恼。 她就那樣靜靜地躺著,像睡著了一般岩睁。 火紅的嫁衣襯著肌膚如雪钞脂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天捕儒,我揣著相機與錄音冰啃,去河邊找鬼。 笑死刘莹,一個胖子當著我的面吹牛阎毅,可吹牛的內容都是我干的。 我是一名探鬼主播点弯,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼扇调,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抢肛?” 一聲冷哼從身側響起狼钮,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤碳柱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后熬芜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莲镣,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年涎拉,在試婚紗的時候發(fā)現(xiàn)自己被綠了瑞侮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼓拧,死狀恐怖半火,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情季俩,我是刑警寧澤钮糖,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布铺罢,位于F島的核電站薪捍,受9級特大地震影響,放射性物質發(fā)生泄漏登疗。R本人自食惡果不足惜赂韵,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一娱节、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧祭示,春花似錦肄满、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汇陆,卻和暖如春怒炸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毡代。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工阅羹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人教寂。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓捏鱼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親酪耕。 傳聞我的和親對象是個殘疾皇子导梆,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容

  • 一看尼、電量消耗理論與分析 寫出耗電量低的應用的關鍵是要透徹理解它的理論以及全部過程递鹉。下面將對電量消耗的相關理論知識進...
    香沙小熊閱讀 4,590評論 4 4
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,510評論 25 707
  • 我們之前閱讀了《毛姆傳》第十五章“布龍奇諾”男孩。艾倫取代杰拉德成為陪伴在毛姆身邊的人狡忙,毛姆也很依賴和信任他梳虽。 誰...
    杜飛魚的生活筆記閱讀 1,643評論 1 1
  • 北國的雪 南國的雨 紛紛揚揚 衣襟沾濕著 上演的戲就該落幕了 許久以來 這里老得沒有河流 花謝總是從春天開始 筆墨...
    行走的卓瑪閱讀 96評論 0 0
  • 感恩許經(jīng)理,珊珊今天上午一起幫我填銷售記錄灾茁,很快就填完了。 感恩三叔王副總她們帶領大家種菜谷炸,使得我們每天都有新鮮的...
    一丟一丟閱讀 127評論 0 0