耗电优化
耗电检测工具
Battery Historian是一款Google提供的Android系统电量分析工具,能直观显示手机的电量消耗过程。
Battery Historian使用步骤
- 初始化Battery Historian,使用adb命令
adb shell dumpsys batterystats --enable full-wake-history
adb shell dumpsys batterystats --reset
- 初始化后,操作需要测试耗电的场景
- 经过2步骤后,用下面命令将bugreport信息保存为bugreport.txt文件
adb bugreport > bugreport.txt
打开可看到耗电数据,但可读性差,下面转成html
- 生成html报告
python historian.py -a bugreport.txt > battery.html
historian.py需要python环境,historian.py脚本可从github下载
historian.py需要和bugreport.txt在同个目录下
- 使用Chrome打开生成但HTML文件,即可查看详细报告。
报告参数解析
三大模块省电优化
移动设备的耗电主要集中在三个模块:显示(屏幕)、CPU、网络模块(蜂窝网络)
1. 显示
对应用程序来说,在使用时屏幕显示的耗电基本没多少优化空间,但可以根据是否长时间没有操作进入熄屏状态。
2. 网络
通常情况下,Wi-Fi连接网络时的功耗低于使用移动网络的功耗。
使用移动网络传输数据,电量的消耗有以下3中状态:
- Full power:高功率状态,允许最大传输速率操作
- Low power:低功耗状态,电量消耗大概是Full power的50%
- Standby:空闲状态,没有数据传输时,电量消耗最少。
在应用中每创建一个新的网络连接,网络模块都会转换到高功率状态,在传完后回到低功耗,转换过程5秒,最后转空闲态。每个数据传输都会导致20秒的电量消耗。
Wi-Fi的耗电与包率(每秒发送和接受的包数)和通道率(网速)这两个因素有关。
优化网络连接降低电量消耗的方案:
- 尽量在Wi-Fi下使用数据传输(一些不紧急的判断在Wi-Fi环境在传输)
- 使用Wi-Fi传输数据时,应尽可能增大每个包的大小(不超过MTU),并降低并发包的频率。
- 在蜂窝网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求。
- 压缩数据格式,比如使用GZIP压缩传输减少数据量
3. CPU
为了省电和高效管理CPU,Linux内核提供5种变频模式供用户选择使用。但调频需要在Root环境下,应用开发中一般无法使用。在应用中需要注意的是如何减少CPU的开销以达到省电的目的,比如减少计算量。
应用常用优化方案
计算优化
缩短代码产生指令运行的时间,进而减少某个应用程序对CPU时间片的总占用时间。浮点运算比整数运算更消耗CPU时间片,耗电也会增加,所以尽量减少浮点运算
避免浮点运算的方法:
- 除法变乘法
- 充分利用移位
- 查表法,直接使用映射关系,但这个会增加内存开销,视具体场景而定。
避免WakeLock使用不当
在某些场景,比如社交类应用、播放器停留在看歌词页面,需要唤醒手机不要进入息屏睡眠状态。最常用的唤醒手机的方法是使用PowerManager.WakeLock来保持CPU工作并防止屏幕自动变暗关闭。
PowerManager负责对Android设备电源相关进行管理,WakeLock也是一种锁机制,只要应用中有WakeLock,通过相应参数去获取对应的锁,即可达到电源管理目的。
获取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后,可以保持完成需要完成的事,但完成后,或者离开这个场景后,需要及时释放WakeLock,否则会带来不可预估的电量消耗。释放锁代码:
private void releaseWakeLock(){
if(null != mWakeLock){
mWakeLock.release();
mWakeLock = null;
}
}
使用JobScheduler
在Android5.0提供来一个JobScheduler组件,只有在一系列的预置条件满足时,才执行对应的操作,这样既能省电,有保证来功能的完整性。可以在以下场景中考虑使用JobScheduler:
- 重要不紧急的任务,如定期数据库数据更新和数据上报
- 耗电较大的任务,比如充电是才执行数据库备份
- 不紧急可以不执行的网络任务,如可在Wi-Fi下才执行预加载数据
- 可以批量执行的任务
使用JobScheduler的两个步骤:
- 创建JobScheduler
创建JobScheduler,用来初始化一个JobScheduler以及设置触发JobScheduler执行任务的条件。
- 通过getSystemService()获取一个JobScheduler对象
private JobScheduler mJS = null;
public JobSchedulerManager(Context ctx){
mContext = ctx;
mJS = (JobScheduler)mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
}
- 创建一个JobInfo,描述一个任务的执行ID,以及触发这个任务的条件
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;
}
}
- 创建JobSchedulerService继承JobService
public class JobSchedulerService extends JobService{
@Override
public boolean onStartJob(JobParameters params){
//执行具体的任务,最好在异步线程
return false;
}
@Override
public boolean onStopJob(JobParameters params){
//取消一个任务
return false;
}
}
在AndroidManifest.xml中注册服务
JobSchedulerService必须实现两个方法onStartJob(JobParameters params)和onStopJob(JobParameters params)
- 任务开始时,执行onStartJob(JobParameters params)方法,触发已经被执行的任务。执行完毕,需要调用jobFinished(JobParameters params, boolean needsReschedule)来通知系统。
- 系统接受到取消请求,调用onStopJob(JobParameters params)方法取消正在等待执行的任务。
注意:JobService运行在主线程,如果耗时任务,要异步操作来执行。
本文参考书籍《Android应用性能优化最佳实践》