Android后台耗电分析及优化
原文見: 在路上的blog
Android后臺耗電分析及優(yōu)化
- 一、什么是耗電優(yōu)化?
- 二、耗電優(yōu)化第一個方向:優(yōu)化后臺耗電
-
- 1、喚醒鎖定操作卡住(前臺&后臺)
- 2、喚醒次數(shù)過多
- 3、WLAN掃描次數(shù)過多(后臺)
- 4、后臺移動網(wǎng)絡(luò)使用量過高
- 三、耗電優(yōu)化第二個方向:讓系統(tǒng)認(rèn)為是正常耗電
-
- (1)海外應(yīng)用
- (2)國內(nèi)應(yīng)用之華為后臺資源紅線標(biāo)準(zhǔn)
- (3)經(jīng)驗性總結(jié)規(guī)則
- 四、耗電監(jiān)控
-
- 1、google vitals不適合
- 2、合適的耗電監(jiān)控方式
-
- (1)解析bugreport
- (2)Java Hook
- (3)插樁
- 五、名次解釋
主要參考資料:
- Android程序性能優(yōu)化之耗電優(yōu)化
- Android vitals管理中心文檔
開源后臺耗電分析工具: battery_alalyze
一、什么是耗電優(yōu)化?
在實踐中,如果我們的應(yīng)用需要播放視頻、獲取GPS信息、需要拍照,這些耗電看起來是無法避免的。
如果發(fā)現(xiàn)某個應(yīng)用沒怎么使用(前臺時間很少),但是耗電卻非常多。這種情況會跟用戶的預(yù)期差別很大,這種情況就需要優(yōu)化。
二、耗電優(yōu)化第一個方向:優(yōu)化后臺耗電
根據(jù)Android Vitals定義,影響后臺耗電的動作如下:
- 喚醒鎖定操作卡住
- 喚醒鎖定操作卡住(后臺)
- 喚醒次數(shù)過多
- WLAN 掃描次數(shù)過多(后臺)
- 網(wǎng)絡(luò)使用量過高(后臺)
1、喚醒鎖定操作卡住(前臺&后臺)
應(yīng)用會通過調(diào)用帶有 PARTIAL_WAKE_LOCK 標(biāo)記的 acquire() 來獲取部分喚醒鎖定。當(dāng)您的應(yīng)用在后臺運行時,如果部分喚醒鎖定保持了較長時間,則會變?yōu)榭ㄗ顟B(tài)(用戶看不到應(yīng)用的任何部分)。 它會阻止設(shè)備進(jìn)入低功耗狀態(tài)。部分喚醒鎖定僅應(yīng)在必要時使用,并且在不再需要時立即釋放。
Android Vitals 報告部分喚醒鎖定卡住的條件是在以下任一時段內(nèi)至少發(fā)生了一次時長達(dá) 1 小時的部分喚醒鎖定:
(1)所有情況下至少 0.70% 的電池工作時段
或
(2)僅在后臺運行時至少 0.10% 的電池工作時段
喚醒鎖定操作卡住的問題發(fā)現(xiàn)和修復(fù)建議
2、喚醒次數(shù)過多
喚醒是 AlarmManagerAPI 中的一種機制,可讓開發(fā)者設(shè)置鬧鐘以在指定時間喚醒設(shè)備。為設(shè)置喚醒鬧鐘,您的應(yīng)用會調(diào)用 AlarmManager 中某個帶有 RTC_WAKEUP 或 ELAPSED_REALTIME_WAKEUP 標(biāo)記的 set() 方法。當(dāng)喚醒鬧鐘觸發(fā)時,設(shè)備會在執(zhí)行鬧鐘的 onReceive() 或 onAlarm() 方法期間退出低功耗模式并保持部分喚醒鎖定。如果喚醒鬧鐘觸發(fā)次數(shù)過多,則可能會耗盡設(shè)備的電池電量。
喚醒次數(shù)過多標(biāo)準(zhǔn):用戶遇到每小時 10 次以上喚醒的電池工作時段數(shù)百分比。
- Vital 詳細(xì)信息:
- 受影響的工作時段數(shù):用戶遇到每小時 10 次以上喚醒的電池工作時段數(shù)百分比。電池會話是指設(shè)備在兩次充滿電之間的間隔時間。Google 僅會在設(shè)備未充電時收集這項數(shù)據(jù)。
- 會話數(shù):系統(tǒng)已記錄的會話的大概數(shù)量。
- 第 90/99 個百分位:10%/1% 的每日工作時段中用戶每小時遇到喚醒次數(shù)高于顯示的值。
最低 25%:如果您的應(yīng)用發(fā)生問題的工作時段比例等于或高于顯示的閾值,則系統(tǒng)會將此應(yīng)用歸在這項指標(biāo)的最低 25% 區(qū)間(依據(jù)為 Google Play 上前 1000 個熱門應(yīng)用,按安裝量統(tǒng)計)。
喚醒過多修復(fù)及建議
3、WLAN掃描次數(shù)過多(后臺)
當(dāng)應(yīng)用在后臺執(zhí)行 WLAN 掃描時,它會喚醒 CPU,從而加快耗電速度。掃描次數(shù)過多時,設(shè)備的電池續(xù)航時間可能會明顯縮短。如果某個應(yīng)用處于 PROCESS_STATE_BACKGROUND 或 PROCESS_STATE_CACHED 狀態(tài),則會被視為在后臺運行。
WLAN 掃描次數(shù)過多的標(biāo)準(zhǔn):在后臺運行時,應(yīng)用在 0.10% 的電池工作時段內(nèi)每小時執(zhí)行的掃描超過 4 次。
建議:如果可能,您的應(yīng)用執(zhí)行 WLAN 掃描時應(yīng)該是在前臺運行。前臺服務(wù)會自動顯示通知;在前臺執(zhí)行 WLAN 掃描,從而讓用戶知道設(shè)備上發(fā)生 WLAN 掃描的原因和時間。
掃描次數(shù)過多優(yōu)化:如果您的應(yīng)用無法避免在后臺運行期間執(zhí)行 WLAN 掃描,則可能適合采用偷懶至上策略。“偷懶至上”包含三種可用于消減 WLAN 掃描次數(shù)的方法:“減少”、“推遲”和“合并”。如需了解這些方法,請參閱針對電池續(xù)航時間進(jìn)行優(yōu)化。
4、后臺移動網(wǎng)絡(luò)使用量過高
當(dāng)應(yīng)用在后臺連接移動網(wǎng)絡(luò)時,應(yīng)用會喚醒 CPU 并開啟無線裝置。如果反復(fù)執(zhí)行此操作,可能會耗盡設(shè)備的電池電量。如果某個應(yīng)用處于 PROCESS_STATE_BACKGROUND 或 PROCESS_STATE_CACHED 狀態(tài),則會被視為在后臺運行。
后臺網(wǎng)絡(luò)使用量過高的標(biāo)準(zhǔn):在后臺運行時,應(yīng)用在 0.10% 的電池工作時段內(nèi)每小時發(fā)送和接收的數(shù)據(jù)合計達(dá) 50 MB。
建議:可以將應(yīng)用的移動網(wǎng)絡(luò)使用量移至前臺,提醒用戶目前正在進(jìn)行下載,并為他們提供暫停或停止下載的控件。為此,請調(diào)用 DownloadManager 并根據(jù)情況設(shè)置 setNotificationVisibility(int)。
三、耗電優(yōu)化第二個方向:讓系統(tǒng)認(rèn)為是正常耗電
如何讓系統(tǒng)認(rèn)為是正常耗電呢?當(dāng)耗電指標(biāo)低于規(guī)則時,系統(tǒng)也就認(rèn)為是正常耗電了。
(1)海外應(yīng)用
海外應(yīng)用主要參考Google Vitals的規(guī)則。
對于Google Vitals的后臺耗電過多統(tǒng)計規(guī)則中的電池工作時段百分比,對于質(zhì)量評估來看,較難把握。所以主要關(guān)注規(guī)則的具體指標(biāo),即相對更嚴(yán)格的質(zhì)量要求:
(2)國內(nèi)應(yīng)用之華為后臺資源紅線標(biāo)準(zhǔn)
(3)經(jīng)驗性總結(jié)規(guī)則
對于國內(nèi)應(yīng)用來說,目前還沒有非常通用且權(quán)威的后臺耗電規(guī)則,根據(jù)經(jīng)驗,我們將監(jiān)控的內(nèi)容抽象成規(guī)則。
當(dāng)然不同應(yīng)用監(jiān)控的事項或者參數(shù)都不太一樣。由于每個應(yīng)用的具體情況都不太一樣。
下面是一些可以用來參考的簡單規(guī)則。
四、耗電監(jiān)控
那我們的耗電監(jiān)控系統(tǒng)應(yīng)該監(jiān)控哪些內(nèi)容,怎么樣才能比 Android Vitals 做得更好呢?
- 監(jiān)控信息:簡單來說系統(tǒng)關(guān)心什么,我們就監(jiān)控什么,而且應(yīng)該以后臺耗電監(jiān)控為主。類似 Alarm wakeup、WakeLock、WiFi scans、Network 都是必須的,其他的可以根據(jù)應(yīng)用的實際情況。如果是地圖應(yīng)用,后臺獲取 GPS 是被允許的;如果是計步器應(yīng)用,后臺獲取 Sensor 也沒有太大問題。
- 現(xiàn)場信息:監(jiān)控系統(tǒng)希望可以獲得完整的堆棧信息,比如哪一行代碼發(fā)起了 WiFi scans、哪一行代碼申請了 WakeLock 等。還有當(dāng)時手機是否在充電、手機的電量水平、應(yīng)用前臺和后臺時間、CPU 狀態(tài)等一些信息也可以幫助我們排查某些問題。
1、google vitals不適合
缺點:
- 耗電規(guī)則無法修改
- 無法拿到堆棧和其他電池信息
- 國內(nèi)應(yīng)用無法使用
2、合適的耗電監(jiān)控方式
(1)解析bugreport
通常大家可能會使用Battery Historian來分析后臺耗電,但是不夠靈活。比如需要人工查看各資源使用情況及是否達(dá)標(biāo)。所以用python實現(xiàn)了一個簡單的分析bugreport文件的小工具;
核心代碼是剛做測開半年左右寫的,比較亂且水平有限,大家輕拍,也歡迎大家參與優(yōu)化。
- 實現(xiàn)邏輯:
- 重置電池統(tǒng)計信息和歷史記錄(dumpsys batterystats --reset)
- 打開詳細(xì)的wakelock數(shù)據(jù)開關(guān),日志量較大,一般可正常保存3個小時以內(nèi)。
- dumpsys batterystats --enable full-wake-history --啟用
- dumpsys batterystats --disable full-wake-history --關(guān)閉
- 導(dǎo)出bugreport文件
- Android 7.0 and higher: adb bugreport > bugreport.zip
- Android 6.0 and lower: adb bugreport > bugreport.txt
- 利用battery_analyze生成后臺耗電報告
(2)Java Hook
Hook 方案的好處在于使用者接入非常簡單,不需要去修改自己的代碼。下面我以幾個比較常用的規(guī)則為例,看看如果使用 Java Hook 達(dá)到監(jiān)控的目的。
- WakeLock:WakeLock 用來阻止 CPU、屏幕甚至是鍵盤的休眠。類似 Alarm、JobService 也會申請 WakeLock 來完成后臺 CPU 操作。WakeLock 的核心控制代碼都在PowerManagerService中,實現(xiàn)的方法非常簡單。
// 代理 PowerManagerService
ProxyHook().proxyHook(context.getSystemService(Context.POWER_SERVICE), "mService", this);@Override
public void beforeInvoke(Method method, Object[] args) {// 申請 Wakelockif (method.getName().equals("acquireWakeLock")) {if (isAppBackground()) {// 應(yīng)用后臺邏輯,獲取應(yīng)用堆棧等等 } else {// 應(yīng)用前臺邏輯,獲取應(yīng)用堆棧等等}// 釋放 Wakelock} else if (method.getName().equals("releaseWakeLock")) {// 釋放的邏輯 }
}
- Alarm:Alarm 用來做一些定時的重復(fù)任務(wù),它一共有四個類型,其中ELAPSED_REALTIME_WAKEUP和RTC_WAKEUP類型都會喚醒設(shè)備。同樣,Alarm 的核心控制邏輯都在AlarmManagerService中,實現(xiàn)如下:
// 代理 AlarmManagerService
new ProxyHook().proxyHook(context.getSystemService
(Context.ALARM_SERVICE), "mService", this);public void beforeInvoke(Method method, Object[] args) {// 設(shè)置 Alarmif (method.getName().equals("set")) {// 不同版本參數(shù)類型的適配,獲取應(yīng)用堆棧等等// 清除 Alarm} else if (method.getName().equals("remove")) {// 清除的邏輯}
}
- 其他:對于后臺 CPU,我們可以使用卡頓監(jiān)控相關(guān)的方法。對于后臺網(wǎng)絡(luò),同樣我們可以通過網(wǎng)絡(luò)監(jiān)控相關(guān)的方法。對于 GPS 監(jiān)控,我們可以通過 Hook 代理LOCATION_SERVICE。對于 Sensor,我們通過 Hook SENSOR_SERVICE中的“mSensorListeners”,可以拿到部分信息。
通過 Hook,我們可以在申請資源的時候?qū)⒍褩P畔⒈4嫫饋怼.?dāng)我們觸發(fā)某個規(guī)則上報問題的時候,可以將收集到的堆棧信息、電池是否充電、CPU 信息、應(yīng)用前后臺時間等輔助信息也一起帶上。
(3)插樁
雖然使用 Hook 非常簡單,但是某些規(guī)則可能不太容易找到合適的 Hook 點。而且在 Android P 之后,很多的 Hook 點都不支持了。
出于兼容性考慮,我首先想到的是寫一個基礎(chǔ)類,然后在統(tǒng)一的調(diào)用接口中增加監(jiān)控邏輯。以 WakeLock 為例:
public class WakelockMetrics {// Wakelock 申請public void acquire(PowerManager.WakeLock wakelock) {wakeLock.acquire();// 在這里增加 Wakelock 申請監(jiān)控邏輯}// Wakelock 釋放public void release(PowerManager.WakeLock wakelock, int flags) {wakelock.release();// 在這里增加 Wakelock 釋放監(jiān)控邏輯}
}
Facebook 也有一個耗電監(jiān)控的開源庫Battery-Metrics,它監(jiān)控的數(shù)據(jù)非常全,包括 Alarm、WakeLock、Camera、CPU、Network 等,而且也有收集電量充電狀態(tài)、電量水平等信息。
Battery-Metrics 只是提供了一系列的基礎(chǔ)類,在實際使用中,接入者可能需要修改大量的源碼。但對于一些第三方 SDK 或者后續(xù)增加的代碼,我們可能就不太能保證可以監(jiān)控到了。這些場景也就無法監(jiān)控了,所以 Facebook 內(nèi)部是使用插樁來動態(tài)替換。
遺憾的是,Facebook 并沒有開源它們內(nèi)部的插樁具體實現(xiàn)方案。大家可以自行搜索不同插樁方案的實現(xiàn)。
插樁方案使用起來兼容性非常好,并且使用者也沒有太大的接入成本。但是它并不是完美無缺的,對于系統(tǒng)的代碼插樁方案是無法替換的,例如 JobService 申請 PARTIAL_WAKE_LOCK 的場景。
五、名次解釋
- 電池工作時段:是指兩次電池充滿電的時間間隔。
總結(jié)
以上是生活随笔為你收集整理的Android后台耗电分析及优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【5】C++语法与数据结构之STL_li
- 下一篇: 博雅互动预计上半年纯利同比增超130%