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