电量的优化在app开发过程中,往往会被忽视,当在实际使用过程中,用户会很关心app的耗电量,耗电量高的应用会使用户对应用产生质疑,甚至卸载应用
检测工具安装
使用battery-historian来监测电量的情况,battery-historian时Google的一个开源项目
具体安装过程参见:https://github.com/google/battery-historian
当出现下列画面,说明已经开启
其开启成功以后,访问网页如下所示
说明:这里使用的是一台国外的VPS服务器,原本是想在本地虚拟机实验的,一直连接超时,就换成了VPS了,在本地的话可以编译源代码得到测试环境,以下是本地得到环境的示例
检测工具的效果
如果是7.0及以上Android版本,使用
adb bugreport > bugreport.zip
6.0及以下使用
adb bugreport > bugreport.txt
下面是我手里面的两部手机抓取到的报告(红米4和一加5)
检测工具的使用
数据准备
battery-historian
工具需要使用bugreport
中的Battery History
1.先断开adb服务,然后开启adb服务
adb kill-server
这一步很重要,因为当我们开发时做电量记录时会打开很多可能造成冲突的东西。为了保险起见我们重启adb
adb devices
就会自动连接查找手机。当然也可以adb start-server
2.重置电池数据收集数据,我们在开始的时候需要通过以下命令来打开电池数据的获取以及重置:
有时候会产生错误,此时重启手机就好了
adb shell dumpsys batterystats --enable full-wake-history
adb shell dumpsys batterystats --reset
上面的操作相当于初始化操作,如果不这么做会有一大堆的干扰的数据,看起来会比较痛苦。然后把数据线直接拔掉(防止数据线造成充放电数据干扰),现在做一些测试,手动或者跑一些自动化的case都行。经过一段时间后,我们重新连接手机确认adb连上了,运行下面这条命令来将bugreport的信息保存
adb bugreport > bugreport.txt[bugreport.zip]
txt转化为html
可以使用工具将txt转化为html,方便在本地阅读
python historian.py -a bugreport.txt > battery.html
转换后生成一个html文件,使用浏览器打开即可
historian.py
位于script目录下
此时就可以在本地浏览信息了
historian参数说明
横坐标
时间轴
纵坐标
battery_level
:电量,可以看出电量的变化
plugged
:充电状态,这一栏显示是否进行了充电,以及充电的时间范围
screen
:屏幕是否点亮,这一点可以考虑到睡眠状态和点亮状态下电量的使用信息
top
:该栏显示当前时刻哪个app处于最上层,就是当前手机运行的app,用来判断某个app对手机电量的影响,这样也能判断出该app的耗电量信息。该栏记录了应用在某一个时刻启动,以及运行的时间,这对我们比对不同应用对性能的影响有很大的帮助
wake_lock*
:该属性是记录wake_lock模块的工作时间。是否有停止的时候等
running
:界面的状态,主要判断是否处于idle的状态。用来判断无操作状态下电量的消耗
wake_lock_in
:wake_lock有不同的组件,这个地方记录在某一个时刻,有哪些部件开始工作,以及工作的时间
gps
:gps是否开启
phone_in_call
:是否进行通话
Sync
:是否跟后台同步
Job
:后台的工作,比如服务service的运行。从下面图中可以看到qihoo的AppStore和鲁大师都在运行后台服务
data_conn
:数据连接方式的改变,上面的edge是说明采用的gprs的方式连接网络的。此数据可以看出手机是使用2g,3g,4g还是wifi进行数据交换的。这一栏可以看出不同的连接方式对电量使用的影响
statu
:电池状态信息,有充电,放电,未充电,已充满,未知等不同状态
phone_signal_strength
:手机信号状态的改变,这一栏记录手机信号的强弱变化图,依次来判断手机信号对电量的影响
health
:电池健康状态的信息,这个信息一定程度上反映了这块电池使用了多长时间
plug
:充电方式usb或者插座,以及显示连接的时间,这一栏显示了不同的充电方式对电量使用的影响
电量优化的入手点
- 有些耗电的操作可以保留到手机充电时候再作处理
private boolean checkForPower(){
//获取电池的充电状态
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = this.registerReceiver(null, filter);
int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usb = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;//usb充电
boolean ac = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;//交流电
boolean wireless = false;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
wireless = chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS;//无线充电,API>=17
}
return (usb || ac || wireless);
}
- 有些操作可以放到WIFI连接以后去操作
//判断网络连接
private boolean isNetWorkConnected() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return (activeNetworkInfo != null && activeNetworkInfo.isConnected());
}
- 系统为了节省电量,CPU在没有任务忙的时候就会自动进入休眠,有任务需要唤醒CPU高效执行的时候,就会给CPU加wake_lock锁
wake_lock锁主要是相对系统的休眠而言的,意思就是我的程序给CPU加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行
PowerManager pw;
PowerManager.WakeLock mWakeLock;
pw = (PowerManager)getSystemService(POWER_SERVICE);
mWakeLock = pw.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "wakelock");
mWakeLock.acquire();//唤醒CPU
···
if(mWakeLock.isHeld()){
mwakeLock.release();//释放锁
}
使用权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
- 大量高频次的CPU唤醒及操作,我们最好把这些操作集中处理
我们可以采取一些算法来解决:JobScheduler/GCM