耗電優(yōu)化
耗電檢測(cè)工具
Battery Historian是一款Google提供的Android系統(tǒng)電量分析工具,能直觀顯示手機(jī)的電量消耗過(guò)程傍菇。
Battery Historian使用步驟
- 初始化Battery Historian谍椅,使用adb命令
adb shell dumpsys batterystats --enable full-wake-history
adb shell dumpsys batterystats --reset
- 初始化后误堡,操作需要測(cè)試耗電的場(chǎng)景
- 經(jīng)過(guò)2步驟后,用下面命令將bugreport信息保存為bugreport.txt文件
adb bugreport > bugreport.txt
打開(kāi)可看到耗電數(shù)據(jù)雏吭,但可讀性差锁施,下面轉(zhuǎn)成html
- 生成html報(bào)告
python historian.py -a bugreport.txt > battery.html
historian.py需要python環(huán)境,historian.py腳本可從github下載
historian.py需要和bugreport.txt在同個(gè)目錄下
- 使用Chrome打開(kāi)生成但HTML文件杖们,即可查看詳細(xì)報(bào)告悉抵。
報(bào)告參數(shù)解析
三大模塊省電優(yōu)化
移動(dòng)設(shè)備的耗電主要集中在三個(gè)模塊:顯示(屏幕)、CPU胀莹、網(wǎng)絡(luò)模塊(蜂窩網(wǎng)絡(luò))
1. 顯示
對(duì)應(yīng)用程序來(lái)說(shuō)基跑,在使用時(shí)屏幕顯示的耗電基本沒(méi)多少優(yōu)化空間,但可以根據(jù)是否長(zhǎng)時(shí)間沒(méi)有操作進(jìn)入熄屏狀態(tài)描焰。
2. 網(wǎng)絡(luò)
通常情況下媳否,Wi-Fi連接網(wǎng)絡(luò)時(shí)的功耗低于使用移動(dòng)網(wǎng)絡(luò)的功耗。
使用移動(dòng)網(wǎng)絡(luò)傳輸數(shù)據(jù)荆秦,電量的消耗有以下3中狀態(tài):
- Full power:高功率狀態(tài)篱竭,允許最大傳輸速率操作
- Low power:低功耗狀態(tài),電量消耗大概是Full power的50%
- Standby:空閑狀態(tài)步绸,沒(méi)有數(shù)據(jù)傳輸時(shí)掺逼,電量消耗最少。
在應(yīng)用中每創(chuàng)建一個(gè)新的網(wǎng)絡(luò)連接瓤介,網(wǎng)絡(luò)模塊都會(huì)轉(zhuǎn)換到高功率狀態(tài)吕喘,在傳完后回到低功耗赘那,轉(zhuǎn)換過(guò)程5秒,最后轉(zhuǎn)空閑態(tài)氯质。每個(gè)數(shù)據(jù)傳輸都會(huì)導(dǎo)致20秒的電量消耗募舟。
Wi-Fi的耗電與包率(每秒發(fā)送和接受的包數(shù))和通道率(網(wǎng)速)這兩個(gè)因素有關(guān)。
優(yōu)化網(wǎng)絡(luò)連接降低電量消耗的方案:
- 盡量在Wi-Fi下使用數(shù)據(jù)傳輸(一些不緊急的判斷在Wi-Fi環(huán)境在傳輸)
- 使用Wi-Fi傳輸數(shù)據(jù)時(shí)闻察,應(yīng)盡可能增大每個(gè)包的大泄敖浮(不超過(guò)MTU),并降低并發(fā)包的頻率辕漂。
- 在蜂窩網(wǎng)絡(luò)下呢灶,最好做到批量執(zhí)行網(wǎng)絡(luò)請(qǐng)求,盡量避免頻繁的間隔網(wǎng)絡(luò)請(qǐng)求钉嘹。
- 壓縮數(shù)據(jù)格式鸯乃,比如使用GZIP壓縮傳輸減少數(shù)據(jù)量
3. CPU
為了省電和高效管理CPU,Linux內(nèi)核提供5種變頻模式供用戶選擇使用隧期。但調(diào)頻需要在Root環(huán)境下飒责,應(yīng)用開(kāi)發(fā)中一般無(wú)法使用赘娄。在應(yīng)用中需要注意的是如何減少CPU的開(kāi)銷以達(dá)到省電的目的仆潮,比如減少計(jì)算量。
應(yīng)用常用優(yōu)化方案
計(jì)算優(yōu)化
縮短代碼產(chǎn)生指令運(yùn)行的時(shí)間遣臼,進(jìn)而減少某個(gè)應(yīng)用程序?qū)PU時(shí)間片的總占用時(shí)間性置。浮點(diǎn)運(yùn)算比整數(shù)運(yùn)算更消耗CPU時(shí)間片,耗電也會(huì)增加揍堰,所以盡量減少浮點(diǎn)運(yùn)算
避免浮點(diǎn)運(yùn)算的方法:
- 除法變乘法
- 充分利用移位
- 查表法鹏浅,直接使用映射關(guān)系,但這個(gè)會(huì)增加內(nèi)存開(kāi)銷屏歹,視具體場(chǎng)景而定隐砸。
避免WakeLock使用不當(dāng)
在某些場(chǎng)景,比如社交類應(yīng)用蝙眶、播放器停留在看歌詞頁(yè)面季希,需要喚醒手機(jī)不要進(jìn)入息屏睡眠狀態(tài)。最常用的喚醒手機(jī)的方法是使用PowerManager.WakeLock來(lái)保持CPU工作并防止屏幕自動(dòng)變暗關(guān)閉幽纷。
PowerManager負(fù)責(zé)對(duì)Android設(shè)備電源相關(guān)進(jìn)行管理式塌,WakeLock也是一種鎖機(jī)制,只要應(yīng)用中有WakeLock友浸,通過(guò)相應(yīng)參數(shù)去獲取對(duì)應(yīng)的鎖峰尝,即可達(dá)到電源管理目的。
獲取WakeLock
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, "TestLockService");
if(null != mWakeLock){
mWakeLock.acquire();
}
}
}
拿到WakeLock后收恢,可以保持完成需要完成的事武学,但完成后祭往,或者離開(kāi)這個(gè)場(chǎng)景后,需要及時(shí)釋放WakeLock火窒,否則會(huì)帶來(lái)不可預(yù)估的電量消耗链沼。釋放鎖代碼:
private void releaseWakeLock(){
if(null != mWakeLock){
mWakeLock.release();
mWakeLock = null;
}
}
使用JobScheduler
在Android5.0提供來(lái)一個(gè)JobScheduler組件,只有在一系列的預(yù)置條件滿足時(shí)沛鸵,才執(zhí)行對(duì)應(yīng)的操作括勺,這樣既能省電,有保證來(lái)功能的完整性曲掰〖埠矗可以在以下場(chǎng)景中考慮使用JobScheduler:
- 重要不緊急的任務(wù),如定期數(shù)據(jù)庫(kù)數(shù)據(jù)更新和數(shù)據(jù)上報(bào)
- 耗電較大的任務(wù)栏妖,比如充電是才執(zhí)行數(shù)據(jù)庫(kù)備份
- 不緊急可以不執(zhí)行的網(wǎng)絡(luò)任務(wù)乱豆,如可在Wi-Fi下才執(zhí)行預(yù)加載數(shù)據(jù)
- 可以批量執(zhí)行的任務(wù)
使用JobScheduler的兩個(gè)步驟:
- 創(chuàng)建JobScheduler
創(chuàng)建JobScheduler,用來(lái)初始化一個(gè)JobScheduler以及設(shè)置觸發(fā)JobScheduler執(zhí)行任務(wù)的條件吊趾。
- 通過(guò)getSystemService()獲取一個(gè)JobScheduler對(duì)象
private JobScheduler mJS = null;
public JobSchedulerManager(Context ctx){
mContext = ctx;
mJS = (JobScheduler)mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
}
- 創(chuàng)建一個(gè)JobInfo宛裕,描述一個(gè)任務(wù)的執(zhí)行ID,以及觸發(fā)這個(gè)任務(wù)的條件
public boolean addJobSchedulerTask(int task_id){
JobInfo.Builder builder = new JobInfo.Builder(task_id, new ComponentName("PackgeName", JobSchedulerService.class.getName()));
switch(task_id){
case 1:
builder.setPeriodic(1000);
break;
case 2:
builder.setPersisted(false);
break;
default:
}
if(mJS != null){
return mJS.schedule(builder.build()) > JobScheduler.RESULT_FAILURE;
}else{
return false;
}
}
- 創(chuàng)建JobSchedulerService繼承JobService
public class JobSchedulerService extends JobService{
@Override
public boolean onStartJob(JobParameters params){
//執(zhí)行具體的任務(wù)论泛,最好在異步線程
return false;
}
@Override
public boolean onStopJob(JobParameters params){
//取消一個(gè)任務(wù)
return false;
}
}
在AndroidManifest.xml中注冊(cè)服務(wù)
<service android:name="JobSchedulerService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
JobSchedulerService必須實(shí)現(xiàn)兩個(gè)方法onStartJob(JobParameters params)和onStopJob(JobParameters params)
- 任務(wù)開(kāi)始時(shí)揩尸,執(zhí)行onStartJob(JobParameters params)方法,觸發(fā)已經(jīng)被執(zhí)行的任務(wù)屁奏。執(zhí)行完畢岩榆,需要調(diào)用jobFinished(JobParameters params, boolean needsReschedule)來(lái)通知系統(tǒng)。
- 系統(tǒng)接受到取消請(qǐng)求坟瓢,調(diào)用onStopJob(JobParameters params)方法取消正在等待執(zhí)行的任務(wù)勇边。
注意:JobService運(yùn)行在主線程,如果耗時(shí)任務(wù)折联,要異步操作來(lái)執(zhí)行粒褒。
本文參考書(shū)籍《Android應(yīng)用性能優(yōu)化最佳實(shí)踐》