Android優(yōu)化之電量?jī)?yōu)化的實(shí)現(xiàn)
Android 5.0 后用 Battery Historian 工具分析電量。
耗電因素
移動(dòng)網(wǎng)絡(luò)請(qǐng)求
手機(jī)通過(guò)內(nèi)置的射頻模塊和基站聯(lián)系,從而鏈接上網(wǎng)的,而這個(gè)射頻模塊(radio)是非常耗電的,為了控制這個(gè)射頻模塊的耗電,硬件驅(qū)動(dòng)及 Android RIL 層做了很多處理。例如可以單獨(dú)關(guān)閉 radio(飛行模式),間歇性假休眠 radio(有數(shù)據(jù)發(fā)生時(shí)才上電,保持一個(gè)頻率的與基站交互)等等。如今的 App 都是移動(dòng)互聯(lián)網(wǎng) App,不可避免的會(huì)有大量的網(wǎng)絡(luò)請(qǐng)求,會(huì)導(dǎo)致 radio 一直處于活躍狀態(tài),從而耗電量增加。
使用移動(dòng)網(wǎng)絡(luò)傳輸數(shù)據(jù),電量的消耗有以下 3 種狀態(tài):
- Full power:高功率狀態(tài),移動(dòng)網(wǎng)絡(luò)連接被激活,允許設(shè)備以最大 的傳輸速率進(jìn)行操作。
- Low power:低功耗狀態(tài),對(duì)電量的消耗差不多是 Full power 狀態(tài)下的 50%。
- Standby:空閑態(tài),沒(méi)有數(shù)據(jù)連接需要傳輸,耗電最少。
從低功率到高功率大約 1.5s,從空閑態(tài)到高功率大約 2s,秒。在應(yīng)用中每創(chuàng)建一個(gè)新的網(wǎng)絡(luò)連接,網(wǎng)絡(luò)(射頻)模塊都會(huì)轉(zhuǎn)換到高功率狀態(tài)(Radio Full Power),在數(shù)據(jù)傳輸完后再轉(zhuǎn)回低功耗狀態(tài)(Radio Low Power),轉(zhuǎn)換的過(guò)程需要 5 秒,這 5 秒的耗電量保持在高功率狀態(tài),最后再轉(zhuǎn)換空閑態(tài)需要 12 秒。因此,對(duì)于一個(gè)典型的移動(dòng)網(wǎng)絡(luò)設(shè)備,每個(gè)數(shù)據(jù)傳輸都會(huì)導(dǎo)致網(wǎng)絡(luò)模塊消耗 20 秒的電量。
WakeLock
Android 系統(tǒng)本身為了優(yōu)化電量的使用,會(huì)在沒(méi)有操作時(shí)進(jìn)入休眠狀態(tài),來(lái)節(jié)省電量。當(dāng)然,為了便于開(kāi)發(fā)(很多應(yīng)用不可避免的希望在滅屏后還能運(yùn)行一些事兒,或是要保持屏幕一直亮著--比如播放視頻),Android 提供了一個(gè) PowerManager.WakeLock 的東西.
我們可以用 WakeLock 來(lái)保持 CPU 運(yùn)行,或是防止屏幕變暗/關(guān)閉,讓手機(jī)可以在用戶不操作時(shí)依然可以做一些事兒。然而,獲取 WakeLock 很容易,釋放不好就會(huì)成為難題,消耗電量。例如獲取了一個(gè) WakeLock 來(lái)保持 CPU 運(yùn)轉(zhuǎn),做一個(gè)復(fù)雜運(yùn)算并將數(shù)據(jù)上傳到后臺(tái)服務(wù)器,然后釋放該 WakeLock。然而這個(gè)過(guò)程可能并不像我們想象的那么快,可能因?yàn)楸热绶?wù)器掛掉,計(jì)算出了異常等等導(dǎo)致 WakeLock 沒(méi)有釋放,CPU 會(huì)一直得不到休眠,而大大增加耗電。
另外,WakeLock 還有 android:keepScreenOn 屬性,還可以讓屏幕常量,這也是耗電大戶。
private void acquireWakeLock(Context ctx) { if (null == mWakeLock) { PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "TestLocknService"); if (null != mWakeLock) { mWakeLock. acquire(); } } }
- PARTIAL_WAKE_LOCK:保持 CPU 正常運(yùn)轉(zhuǎn),屏幕和鍵盤(pán)燈有可能 會(huì)關(guān)閉。
- SCREEN_DIM_WAKE_LOCK:保持 CPU 運(yùn)轉(zhuǎn),允許保持屏幕顯示,但有可能變暗,允許關(guān)閉鍵盤(pán)燈。
- SCREEN_BRIGHT_WAKE_LOCK:保持 CPU 運(yùn)轉(zhuǎn),允許保持屏幕高亮顯示,允許關(guān)閉鍵盤(pán)燈。
- FULL_WAKE_LOCK:保持 CPU 運(yùn)轉(zhuǎn),保持屏幕高亮顯示,鍵盤(pán)燈也保持亮度。
- ACQUIRE_CAUSES_ WAKEUP:強(qiáng)制使屏幕亮起,這種鎖主要用于一些必須通知用戶的操作。
- ON_AFTER_RELEASE:當(dāng)鎖被釋放時(shí),保持屏幕亮起一段時(shí)間。
需要注冊(cè)權(quán)限
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.DEVICE_POWER"/>
GPS
應(yīng)用中經(jīng)常會(huì)用到定位服務(wù),Android 提供了 Network 定位和 GPS 定位。相對(duì)來(lái)說(shuō),GPS 會(huì)精確得多,對(duì)于一些諸如跑步,導(dǎo)航類的應(yīng)用基本會(huì)使用 GPS 定位。然而,GPS 定位也會(huì)消耗大量的電量。
AlarmManager
間隔不能太短。
優(yōu)化建議
優(yōu)化網(wǎng)絡(luò)請(qǐng)求
在蜂窩移動(dòng)網(wǎng)絡(luò)下,最好做到批量執(zhí)行網(wǎng)絡(luò)請(qǐng)求,盡量避免頻繁的間隔網(wǎng)絡(luò)請(qǐng)求,盡量多地保持在 Radio Standby 狀態(tài)。
盡量在 Wi-Fi 環(huán)境下使用數(shù)據(jù)傳輸。
謹(jǐn)慎使用 WakeLock
WakeLock 獲取釋放成對(duì)出現(xiàn)(調(diào)用 release),使用超時(shí) WakeLock,以防出異常導(dǎo)致沒(méi)有釋放。
WakeLock 有一個(gè)接口 setReferenceCounted,用來(lái)設(shè)置 WakeLock 的計(jì)數(shù)機(jī)制,true 為計(jì)數(shù),false 為不計(jì)數(shù),默認(rèn)是 true。所謂計(jì)數(shù)即每一個(gè) acquire 必須對(duì)應(yīng)一個(gè) release;不計(jì)數(shù)則是無(wú)論有多少個(gè) acquire,一個(gè) release 就可以釋放。雖然官方說(shuō)默認(rèn) 是計(jì)數(shù)的,但有的第三方 ROM 做了修改,使默認(rèn)是不計(jì)數(shù)的。
主動(dòng)設(shè)置 wakeLock.setReferenceCounted(false)。
監(jiān)聽(tīng)手機(jī)充電狀態(tài)
BatteryManager 會(huì)發(fā)送一個(gè)包含充電狀態(tài)的持續(xù)廣播,我們可以通過(guò)此廣播獲取充電狀態(tài)和電量詳情。因?yàn)檫@是一個(gè)持續(xù)廣播,無(wú)需寫(xiě) Receiver,可以直接通過(guò) intent 獲取相關(guān)數(shù)據(jù)。
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = context.registerReceiver(null,ifilter); // 設(shè)備正在充電 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS,-1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; // 也可以監(jiān)聽(tīng)充電狀態(tài)的變化,只要設(shè)備連接或斷開(kāi)電源,BatteryManager 就會(huì)廣播相應(yīng)的操作 int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1); boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB; boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
另外頁(yè)可以注冊(cè) Receiver來(lái)監(jiān)聽(tīng)
<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>
Doze and App Standby
Android 6.0 提供了兩個(gè)用來(lái)節(jié)省電量的技術(shù) Doze 和 App Standby。
- Doze 瞌睡。如果設(shè)備閑置了一段較長(zhǎng)時(shí)間,Doze 技術(shù)將通過(guò)延遲后臺(tái)網(wǎng)絡(luò)活動(dòng),CPU 運(yùn)行等來(lái)減少電量損耗。
- App Standy 應(yīng)用待機(jī)。不是最近得到過(guò)用戶使用的 App,App Standy 將延緩這個(gè)應(yīng)用的后臺(tái)網(wǎng)絡(luò)活動(dòng)。
所有 Android 6.0 及以上的設(shè)備上,Doze and App Standby 都會(huì)運(yùn)行??赡軙?huì)影響 App 的運(yùn)行,可以根據(jù)官方文檔適配。
可以在代碼中調(diào)起電量?jī)?yōu)化的設(shè)計(jì)頁(yè)面,讓用戶選擇是否將應(yīng)用加入白名單,以在 Doze 模式下能夠做一些事情。
定位
定位中使用 GPS,及時(shí)關(guān)閉
// Remove the listener you previously added locationManager.removeUpdates(locationListener);
計(jì)算優(yōu)化
縮短代碼產(chǎn)生指令運(yùn)行的時(shí)間,進(jìn)而減少某個(gè)應(yīng)用程序?qū)?CPU 時(shí)間片 的總占用時(shí)間,進(jìn)而減少單位時(shí)間內(nèi)該應(yīng)用程序占整個(gè)系統(tǒng)耗電的百分比。
浮點(diǎn)運(yùn)算比整數(shù)運(yùn)算更消耗 CPU 時(shí)間片,因此耗電也會(huì)增加,在編寫(xiě) 代碼的過(guò)程中應(yīng)該盡量減少浮點(diǎn)運(yùn)算。
- 除法變乘法。
- 充分利用移位。
- 查表法,直接使用映射關(guān)系,但這會(huì)增加內(nèi)存占用,視情況而定。
熄屏后停止一些和 UI 效果有關(guān)的操作,比如動(dòng)畫(huà)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android編程之電池電量信息更新的方法(基于BatteryService實(shí)現(xiàn))
- Android4.4開(kāi)發(fā)之電池低電量告警提示原理與實(shí)現(xiàn)方法分析
- Android電池電量跳變
- Android省電的秘密之JobScheduler
- Android實(shí)現(xiàn)顯示電量的控件代碼
- Android開(kāi)發(fā)教程之電源管理詳解
- Android編程實(shí)現(xiàn)檢測(cè)當(dāng)前電源狀態(tài)的方法
- android電源信息查看(電量、溫度、電壓)實(shí)例代碼
- 如何通過(guò)Battery Historian分析Android APP耗電情況
相關(guān)文章
基于Alarmmanager實(shí)現(xiàn)簡(jiǎn)單鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了基于Alarmmanager實(shí)現(xiàn)簡(jiǎn)單鬧鐘功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android實(shí)現(xiàn)ViewPager無(wú)限循環(huán)效果(一)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)ViewPager無(wú)限循環(huán)效果的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05React Native開(kāi)發(fā)中自動(dòng)打包腳本的實(shí)例代碼
這篇文章主要介紹了React Native開(kāi)發(fā)中自動(dòng)打包腳本的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09Android中使用Vectors(2)繪制優(yōu)美的路徑動(dòng)畫(huà)
這篇文章主要介紹了Android中使用Vectors(2)繪制優(yōu)美的路徑動(dòng)畫(huà)的相關(guān)資料,需要的朋友可以參考下2016-03-03Android開(kāi)發(fā)筆記之:AsyncTask的應(yīng)用詳解
本篇文章是對(duì)Android中AsyncTask的應(yīng)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Android中ACTION_CANCEL的觸發(fā)機(jī)制與滑出子view的情況
這篇文章主要介紹了Android中ACTION_CANCEL的觸發(fā)機(jī)制與滑出子view的情況,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Android動(dòng)畫(huà)之補(bǔ)間動(dòng)畫(huà)(Tween Animation)基礎(chǔ)學(xué)習(xí)
補(bǔ)間動(dòng)畫(huà)是指定開(kāi)始和結(jié)束的圖像狀態(tài),自動(dòng)生成需要顯示的過(guò)度圖像的動(dòng)畫(huà)。補(bǔ)間動(dòng)畫(huà)又分為四種:移動(dòng),縮放,旋轉(zhuǎn),通明度等。下面就來(lái)給大家一篇關(guān)于Android中補(bǔ)間動(dòng)畫(huà)的基礎(chǔ)知識(shí),有需要的可以參考學(xué)習(xí)。2016-09-09Android ApplicationInfo 應(yīng)用程序信息的詳解
這篇文章主要介紹了Android ApplicationInfo 應(yīng)用程序信息的詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10Android Java調(diào)用自己C++類庫(kù)的實(shí)例講解
今天小編就為大家分享一篇關(guān)于Android Java調(diào)用自己C++類庫(kù)的實(shí)例講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02