你是否了解APP耗电问题?深入探索 Android 电量优化,flutter插件推荐
- 3)、最后,把電量輸送給電池,而整個降壓的過程中會產(chǎn)生熱能。
分類
-
1)、高壓低電流快充方案:在充電過程中提升充電電壓(7-20V)來提升充電功率。
-
2)、低壓大電流快充方案:在電壓一定情況下,增加電流,通常使用并聯(lián)電路的方式進行分流。
-
3)、鋁-石墨烯超級電池
-
超高耐用性和安全性,快充充電1.1秒就能充滿電。
-
實驗階段。
3、壽命
通常使用充電循環(huán)次數(shù)衡量。
4、安全性
嚴格控制電池容量,例如 VOOC 就使用了各種安全檢測技術。
5、電量和硬件
-
手機耗電是通過使用相應的硬件模塊來消耗電能。
-
CPU、屏幕、WIFI、數(shù)據(jù)網(wǎng)絡、GPS、音視頻通話在日常耗電量中占比最大。
6、Android 耗電演進
KITKAT
批處理傳感器
分批有效地收集和傳遞傳感器事件。
Alarm 對齊
批處理在合理的相似時間內(nèi)的所有應用的鬧鈴,以便系統(tǒng)僅喚醒一次。
Lollipop
-
開啟 Volta 項目
-
Job Scheduler
-
dumpsys batterystats
-
Battery Historian
-
修復 native fork 進程保活的 bug
Marshmallow
-
省電功能
-
Doze 低功耗模式
-
App Standby 應用待機摸手機
Nougat
-
優(yōu)化省電功能
-
Doze 加強版
-
implicit broadcasts 顯示
-
混合編譯
Oreo
-
更多優(yōu)化省電功能
-
后臺執(zhí)行限制
-
后臺位置限制
P(電壓管理嚴格限制)
應用待機分組(App Standby Bueckets)
-
從應用安裝開始。
-
分組決定后臺被限制的程度。
-
不常用的應用將被限制地更加嚴格。
應用后臺限制(Background Restrictions)
-
用戶開啟。
-
停止后臺運行。
-
提示用戶后臺耗電嚴重的應用,用戶可選擇停止它們的后臺運行。
省電模式(Battery Saver)
-
用戶開啟。
-
所有應用進入待機模式。
-
更加嚴格的后臺限制,而且無視應用的 Target API。
三、電量檢測方案
=================================================================
對于電量的統(tǒng)計有一個公式,如下所示:
模塊電量(mAh) = 模塊電流(mA)* 模塊耗時(h)
Android 系統(tǒng)要求 ROM 廠商必須在 /frameworks/base/core/res/res/xml/power_profile.xml 提供組件的電源配置文件。而 Android 系統(tǒng)的電量計算 PowerProfile 正是通過讀取 power_profile.xml 的數(shù)據(jù)。
1、設置—耗電排行
-
1)、直觀,但沒有詳細數(shù)據(jù),對解決問題幫助不大。
-
2)、需要找特定場景專項測試,比如在某一個界面操作一段時間,然后來判斷這個頁面是否耗電。
2、使用廣播監(jiān)聽電量變化—ACTION_BATTERY_CHANGED
獲取電池電量、充電狀態(tài)、電池狀態(tài)等信息。
實戰(zhàn)案例
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Intent intent = registerReceiver(null, filter);
LogUtils.i("battery " + intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1));
缺點
-
1)、價值不大:針對手機整體的耗電量,而非單個 App。
-
2)、實時性差、精度較低,被動通知。
3、dumpsys batterystats
batterystats 是 Android 5.0 提供的工具,它可以獲取各個 App 的 WakeLock、CPU 時間占用等信息,同時增加了一個 Estimated power use(mAh)功能,預估耗電量。
作用
將電量測量轉(zhuǎn)化為功能模塊的使用時間或者次數(shù)。
adb shell dumpsys batterystats > battery.txt
在 battery.txt 搜索 ‘Estimated power use’ 關鍵字,下面粗略統(tǒng)計了各個 Uid 的總耗電量。
Estimated power use (mAh):
Capacity: 3350, Computed drain: 2767, actual drain: 3752-3853
Uid 1000: 1014 ( cpu=999 wake=1.36 radio=11.4 wifi=1.24 gps=0.435 sensor=0.808 ) Excluded from smearing
Unaccounted: 985 ( ) Including smearing: 0 ( ) Excluded from smearing
Uid 0: 416 ( cpu=157 wake=210 radio=38.8 wifi=9.51 ) Excluded from smearing
…
batterystats 所記錄的電量統(tǒng)計數(shù)據(jù)源自于 BatteryStatsService-電量統(tǒng)計服務,其實現(xiàn)類為 BatteryStatsImpl,內(nèi)部正是使用的 PowerProfile 。
BatteryStatsImpl 為每一個應用創(chuàng)建與之對應的 UID 來監(jiān)控器系統(tǒng)資源的使用情況,其統(tǒng)計了 12 大模塊的電量消耗,如下所示:
-
Camera、Audio、Video
-
Bluetooth、Network、Wakelock
-
Sensor、Radio、Screen
-
WIFI、CPU、GPS
4、Battery Historian
特點
-
1)、查看自設備上次充電以來各種匯總統(tǒng)計信息,而且可以選擇對應的 App 查看詳細信息。
-
2)、可視化展示指標:
-
耗電比例。
-
執(zhí)行時間、次數(shù)。
-
3)、僅適合線下使用。
安裝
-
1)、安裝 Docker
-
2)、docker – run -p:9999 gcr.io/android-battery-historian/stable:3.0 --port 9999 (需要翻墻)
導出電量信息
-
1)、使用 batterystats 命令重置手機電量:adb shell dumpsys batterystats --reset
-
2)、使用 batterystats 命令獲取電池數(shù)據(jù)權限并開啟記錄全面的電量信息:adb shell dumpsys batterystats --enable full-wake-history
-
3)、測試完成后,使用 bugreport 導出電量信息:
-
7.0和7.0以后:adb bugreport bugreport.zip
-
6.0和6.0之前:adb bugreport > bugreport.txt
-
通過 historian 圖形化展示結(jié)果:python historian.py -a bugreport.txt > battery.html
上傳分析
- 1)、打開 http://localhost:
如果打不開,可以使用備用網(wǎng)站 https://bathist.ef.lc/
- 2)、上傳 bugreport 文件,點 Submit 提交即可。
Battery Historian 數(shù)據(jù)分析
Hitorian V2 — 電量統(tǒng)計圖表
Add Metrics
+最新移動架構視頻+大廠安卓面試真題+項目實戰(zhàn)源碼講義》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整內(nèi)容開源分享
img.cn/img_convert/08cae2f8ea15e60a693875b47a2560fe.png)
在 Add Metrics 中我們可以增加更多的測量項。
CPU running
如果一直處于 running,則表明電量消耗比較高。
JobScheduler
選中 Job Scheduler 的某一個工作時間片,我們可以查看具體的 發(fā)生的時間、耗時以及次數(shù),最重要的是它統(tǒng)計出來了是哪一個進程在使用這個 JobScheduler。
App Selection
-
1)、選擇要分析電量的指定 App。
-
2)、點擊右邊區(qū)域的 System Stats 一欄可以在下方查看各個系統(tǒng)組件的電量百分比消耗詳情,例如 Userspace Wakelocks。
主入口處的 Switch to Bugreport Comparison
選擇多個文件進行上傳對比。
5、電量專項測試
1)、耗電場景測試
-
復雜計算。
-
音視頻播放。
2)、傳感器相關
-
使用時長
-
耗電量
-
發(fā)熱
3)、后臺靜默測試
四、耗電優(yōu)化
===============================================================
1、耗電優(yōu)化的難點
-
1)、「缺乏現(xiàn)場,無法復現(xiàn)」。
-
2)、「信息不全,難以定位」。
-
3)、「無法評估結(jié)果」。
在 App 開發(fā)中,經(jīng)常會由于某個需求場景或 代碼 bug 而導致大量耗電。
2、后臺調(diào)度任務省電
思考步驟
-
需要后臺運行
-
長時間下載:DownloadManager
-
數(shù)據(jù)同步:SyncAdapter
-
本地任務:JobScheduler
-
特定時間執(zhí)行:AlarmManager
-
實時通信:推送服務
-
立刻執(zhí)行:Foreground Service
對于耗電優(yōu)化中,我們最常用的就是 JobScheduler,下面👇,我們來實戰(zhàn)一下。
Job Scheduler 實戰(zhàn)
/**
- 開啟 JobScheduler
*/
private void startJobScheduler() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(getPackageName(), JobSchedulerService.class.getName()));
// 設置僅在 充電和WIFI 下才使用 JobScheduler 進行批量任務處理
builder.setRequiresCharging(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
jobScheduler.schedule(builder.build());
}
}
其中,「JobSchedulerService 就是用于進行批量任務處理的服務」,示例代碼如下所示:
/**
- 用于進行批量任務處理的 JobSchedulerService
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class JobSchedulerService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 此處執(zhí)行在主線程
// 模擬一些處理:批量網(wǎng)絡請求,APM日志上報
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
特點
-
1)、「僅支持 API 21 及之上」。
-
2)、「在符合某些條件時創(chuàng)建執(zhí)行在后臺的任務」。
-
3)、「把不緊急的任務放到更合適的時機批量處理」。
符合 Android 規(guī)則,手機在充電狀態(tài)才去做耗電工作。示例代碼如下所示:
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
//獲取用戶是否在充電的狀態(tài)或者已經(jīng)充滿電了
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL;
3、電量優(yōu)化套路總結(jié)
1、優(yōu)化應用的后臺耗電
避免后臺長時間獲取 WakeLock、WIFI 和藍牙的掃描等。
2、符合系統(tǒng)的耗電規(guī)則
Android P 使用了 Android Vitals 監(jiān)控后臺耗電,其規(guī)則如下所示:
-
1)、Alarm Manager wakeup 喚醒過多:當手機不在充電狀態(tài),每小時 wakeup 喚醒次數(shù)大于 10 次。
-
2)、頻繁使用局部喚醒鎖:當手機不在充電狀態(tài),partial wake lock 持有超過1小時。
-
3)、后臺網(wǎng)絡使用量過高:當手機不在充電狀態(tài)而且應用在后臺,每小時網(wǎng)絡使用量超過 50MB。
-
4)、后臺 WiFi scans 過多:當手機不在充電狀態(tài)而且應用在后臺,每小時大于4次 WiFi scans。
3、CPU 時間片
「Android 手機保護 AP 和 BP 兩個 CPU。AP 即 Application Processor,所有的用戶界面以及 App 都是運行在 AP 上的。BP 即 Baseband Processor,手機射頻都是運行在這個 CPU 上的。而一般我們所說的耗電,PowerProfile 文件里面的 CPU,指的是 AP」。
CPU 耗電通常有兩種情況:
-
1)、「長期頻繁喚醒:原本可以僅僅在 BP 上運行,消耗 5mA 左右,但是因為喚醒,AP 就會運作,不同手機情況不一樣,至少會導致 20~30 mA 左右的耗電」。
-
2)、「CPU 長期高負荷:例如 App 退到后臺的時候沒有停止動畫,或者程序有不退出的死循環(huán)等等,導致 CPU 滿頻、滿核地跑」。
常用優(yōu)化 CPU 時間片的方式有:
-
1)、「獲取運行過程線程 CPU 消耗,定位 CPU 占用率異常方法」。
-
2)、「減少后臺應用的主動運行」。
4、網(wǎng)絡相關
通常情況下,使用 WIFI 連接網(wǎng)絡時的功耗要低于使用移動網(wǎng)絡的功耗。而使用移動網(wǎng)絡傳輸數(shù)據(jù),電量的消耗有以下3種狀態(tài):
-
「Full power:高功率狀態(tài),移動網(wǎng)絡連接被激活,允許設備以最大的傳輸速率進行操作」。
-
「Low power:低功耗狀態(tài),對電量的消耗差不多是 Full power 狀態(tài)下的 50%」。
-
「Standby:空閑態(tài),沒有數(shù)據(jù)連接需要傳輸,電量消耗最少」。
因此,為了避免網(wǎng)絡連接所帶來的電量消耗,我們可以采用如下幾種方案:
-
1)、盡量在 WIFI 環(huán)境下進行數(shù)據(jù)傳輸,在使用 WIFI 傳輸數(shù)據(jù)時,應該盡可能增大每個包的大小(不超過 MTU),并降低發(fā)包的頻率。
-
2)、在蜂窩移動網(wǎng)絡下需要對請求時機及次數(shù)控制:可以延遲執(zhí)行的網(wǎng)絡請求稍后一起發(fā)送,最好做到批量執(zhí)行,盡量避免頻繁的間隔網(wǎng)絡請求,以盡量多地保持在 Radio Standby 狀態(tài)。
-
3)、使用 JSON 和 Protobuf 進行數(shù)據(jù)壓縮,減少時間。
-
4)、禁止使用輪詢功能:輪詢會導致網(wǎng)絡請求一直處于被激活的狀態(tài),耗電過高。
5、定位相關
-
1)、「根據(jù)場景謹慎選擇定位模式:對定位準確度沒那么高的場景可以選擇低精度模式」。
-
2)、「可以考慮網(wǎng)絡定位代替 GPS」。
-
3)、「使用后務必及時關閉,減少更新頻率,例如定位開啟一定時間后超過某個閾值可以執(zhí)行一個兜底策略:強制關閉 GPS」。
6、界面相關
-
1)、「離開界面后停止相關活動,例如關閉動畫」。
-
2)、「耗電操作判斷前后臺,如果是后臺則不執(zhí)行相關操作」。
7、WakeLock 相關
WakeLock 常用于后臺播放音視頻、錄制音視頻、下載文件的情況。如果沒有合理使用 WakeLock,則會造成嚴重的耗電問題,為了避免該問題,「我們應該定期針對使用了 WakeLock 的模塊進行重點排查」。
我們可以使用 adb shell dumpsys power 命令查看系統(tǒng)當前的耗電信息,其中我們可以看到 WakeLock 列表,它通常會以**「”mLocks.size“ 或者 ”Wake Locks:size“」** 開頭。關于 WakeLock 的使用我們要著重注意以下幾點:
-
1)、「注意成對使用 acquire、release」。
-
2)、「建議使用帶參數(shù)的 acquire,避免沒有及時釋放而導致電量消耗過大」。
-
3)、「使用 finally 確保 release 一定會被調(diào)用」。
-
4)、「常亮場景使用 keepScreenOn 即可」。
-
5)、「WakeLock 有一個接口 setReferenceCounted,用來設置 WakeLock 的技術機制,官方默認為計數(shù)。true 為計數(shù),false 為不計數(shù)。所謂計數(shù)即每一個 acquire 必須對應一個 release;不計數(shù)則是無論有多少個 acquire,一個 release 就可以釋放。但是問題是有的第三方 ROM 它將默認設置為了不計數(shù),以為我們需要在調(diào)用 newWakeLock 之后再調(diào)用 setReferenceCounted 為 false」。
8、計算優(yōu)化
「浮點運算比整數(shù)運算更消耗 CPU 時間片,因此耗電也會增加」。避開浮點運算的優(yōu)化方法如下所示:
-
1)、「除法變乘法」。
-
2)、「充分利用移位」。
-
3)、「在 native 層開發(fā)時,可以利用 ARM neon 指令集做并行運算,注意需要 ARM V7 及以上架構 CPU 才能支持」。
9、滅屏時停止動畫
「我們可以監(jiān)聽滅屏以及亮屏的廣播,在滅屏的時候停止 surfaceView 的動畫繪制。在亮屏的時候,恢復動畫的繪制」。
五、耗電監(jiān)控
===============================================================
以后臺耗電監(jiān)控為主,必須監(jiān)控的模塊有:
-
1)、「Alarm wakeup」
-
2)、「WakeLock」
-
3)、「WiFi scans」
-
4)、「Network」
「必須監(jiān)控的現(xiàn)場信息有」 :
-
1)、「堆棧信息」
-
2)、「是否充電」
-
3)、「電量水平」
-
4)、「應用前后臺時間」
-
5)、「CPU 狀態(tài)信息」
最后,我們需要 「提煉規(guī)則,將監(jiān)控內(nèi)容 => 抽象成規(guī)則」。
1、Java Hook
我們可以通過代理對應的 Service 實現(xiàn),完成收集 Wakelock、Alarm、GPS 的申請堆棧、釋放信息、手機充電狀態(tài)等等。
?
示例項目
?
2、電量輔助監(jiān)控實戰(zhàn)
1)、獲取運行時能耗文件
-
1)、adb pull /system/framework/framework-res.apk
-
2)、反編譯,xml—》power_profile
2)、電量輔助監(jiān)控
線下使用 epic 進行 AOP 電量輔助統(tǒng)計
這里我們就以 WakeLock 的監(jiān)控為例,切面代碼如下所示:
public static long sStartTime = 0;
@Insert(value = “acquire”)
@TargetClass(value = “com.optimize.performance.wakelock.WakeLockUtils”,scope = Scope.SELF)
public static void acquire(Context context){
trace = Log.getStackTraceString(new Throwable());
sStartTime = System.currentTimeMillis();
Origin.callVoid();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
WakeLockUtils.release();
}
},1000);
}
@Insert(value = “release”)
@TargetClass(value = “com.optimize.performance.wakelock.WakeLockUtils”,scope = Scope.SELF)
public static void release(){
LogUtils.i(“PowerManager “+(System.currentTimeMillis() - sStartTime)+”/n”+trace);
此外,我們也可以利用 epic 來監(jiān)控每個線程的執(zhí)行時間,超過閾值則警告,示例代碼如下所示:
public static long runTime = 0;
@Insert(value = “run”)
@TargetClass(value = “java.lang.Runnable”,scope = Scope.ALL)
public void run(){
runTime = System.currentTimeMillis();
Origin.callVoid();
LogUtils.i("runTime "+(System.currentTimeMillis() - runTime));
}
3、編譯插樁
「寫一個基礎類,然后在統(tǒng)一的調(diào)用接口中添加監(jiān)控邏輯」。這里我們可以參考 Facebook Battery-Metrics 獲取、監(jiān)控數(shù)據(jù)的方式。其代碼如下所示:
public class WakelockMetrics {
/**
-
獲取 WakeLock
-
@param wakeLock WakeLock
-
@param timeout 超時時間
*/
public static void acquire(PowerManager.WakeLock wakeLock, long timeout) {
wakeLock.acquire(timeout);
// 監(jiān)控 wakelock 相關信息
Log.e(“HOOOOOOOOK”, “–acquireWakeLock–”);
總結(jié)
以上是生活随笔為你收集整理的你是否了解APP耗电问题?深入探索 Android 电量优化,flutter插件推荐的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2018年成功IPO的科技独角兽里,你最
- 下一篇: bootstrap-table+boot