版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
手机各个硬件模块的耗电量是不一样的,有些模块非常耗电比如移动蜂窝网络、GPS,而有些模块则相对显得耗电量小很多。
电量测试就是测试移动设备电量消耗快慢的一种测试方法。一般用平均电流来衡量电量消耗速度。平均电流越小,说明设备使用时间越长。但是平均电流多大才说明不耗电却没有一个统一标准。
电量测试从代码层面上讲,可分析下不大,只能说在写代码的时候注意某一些点,我们需要使用其他办法来进行电量测试,分有硬件测试和软件测试。
硬件测试:
利用硬件设备测试被测设备的电流,统计一段时间内(使用某个功能模块)的平均电流值。其实就是一个万用表(电流表),对每个时刻的耗电量进行测试。
软件测试:
利用系统工具导出分析报告,查看报告相关耗电点。
硬件测试这边就不讲解,主要讲一下软件测试。
注:如果有条件使用硬件测试是最好的,可以看到每一个界面,每一个按钮的耗电情况,针对性的进行优化。软件测试没有特别好的方案,只能是查看比较大的耗电模块,征对一些点进行避免。
Battery Historian 是 google 开源的电池历史数据分析工具。
github 地址
1.安装环境
需要先安装 Go、Python、Git 以及 java ,同时配置环境变量。
下载地址:Go、Python、Git。
2.下载 Battery Historian
运行命令: go get -d -u github.com/google/battery-historian/…
需要等待一小段时间,会下载到当前用户目录下有个 go 文件夹下存在。
3.编译及运行
在 cmd 中打开文件所在下的 src\github.com\google\battery-historian 目录,运行命令 :go run setup.go 。(第一次运行这个命令等待时间比较久)。
然后运行命令:go run cmd/battery-historian/battery-historian.go 进行启动。这时候可以直接通过网址 localhost:9999 进行访问。
这边提供一个其他人搭建好的链接,可以直接使用。
Battery Historian
使用安卓自带的 bugreport 导出分析数据。(从 SDK25 开始, bugreport 必须在 7.0 以上设备才能使用。对于 7.0 之前的设备,我们必须下载旧版本的工具进行导出 )
SDK 各个版本下载链接
adb shell dumpsys batterystats --reset
在准备进行测试前,先运行清除命令,清空原先的无用电量日志信息,避免对测试结果进行影响。
adb shell dumpsys batterystats --enable full-wake-history
开启更完整的 wakelock 信息,有更详细的电量信息。
拔掉USB(让设备不处于充电状态),运行程序一段时间后,然后执行命令去获取报告:
安卓版本大于 7.0: adb bugreport bugreport.zip
安卓版本小于等于 6.0: adb bugreport > bugreport.txt
上传日志信息,点击提交进行分析。
这是全系统的电量消耗分析,对于我们应用来说作用不大,我们可以在下方的 App Selection 中选择我们的应用,这样就会显示我们选中应用的电量分析。
Reboot 表示系统启动与否,也就是开关机状态,绿色部分是由于处于关机状态。
在分析图中有一条黑色的线,这个是系统整体的电量。图中黑线不停的下降,是由于使用,电量在不停的消耗,直到底部,即消耗完毕,所以关机(Reboot 为绿色)。后续上升是插上充电器进行充电操作。
当鼠标移上去时候,选择部分会变红,同时会显示出选中这段电量的详细信息,有电量水平的变化区间,以及具体时间。这些信息是只要鼠标在这张图上都可以显示出来。
Battery Level 上有很多小点,这些小点表示电量的变化,正常变化量是 1。
鼠标移到这些电量变化的小点上,可以查看更详细的信息。最上方红色框框表示当前电量变化的时间,在这个下方显示的另一个时间,表示统计开始到现在的时间(使用上面的指令会进行重置),下方的红色框框中 12 表示的是当前的电量。
WakeLock 是一种锁的机制,只要有应用拿着这个锁,CPU就无法进入休眠状态,一直处于工作状态。当鼠标移上去的时候,可以看见一个名为 location_lock 的 WakeLock 存在这,导致 cpu 无法休息。
网络连通性主要是显示当前使用的网络状态,鼠标移动上去,可以发现,浅蓝色的表示使用网络状态为 wifi,深蓝色的表示使用的是蜂窝网络。
对比表示电量的黑线,可以发现,在使用蜂窝网络的时候,电量明显加快消耗。说明蜂窝网络比 wifi 更耗电。
这边附一份 battery-historian V2.0 的其他参数。
CPU runing CPU 运行的状态,是否被唤醒
Kernel only uptime 只有内核运行时间
Activity Manager Proc 活跃的用户进程
Mobile network type 网络类型
Mobile radio active 移动蜂窝信号 BP侧耗电
Crashes(logcat) 某个时间点出现 crash 的应用
Doze 是否进入 Doze 模式
Device active 和 Doze 相反
JobScheduler 异步作业调度
SyncManager 同步操作
Temp White List 电量优化白名单
Phone call 是否打电话
GPS 是否使用GPS
Network connectivity 网络连接状态(wifi、mobile是否连接)
Mobile signal strength 移动信号强度(great\good\moderate\poor)
Wifi scan 是否在扫描 wifi 信号
Wifi supplicant 是否有 wifi 请求
Wifi radio 是否正在通过 wifi 传输数据
Wifi signal strength wifi 信号强度(great\good\moderate\poor)
Wifi running wifi 组件是否在工作(未传输数据)
Wifi on 同上
Audio 音频是否开启
Camera 相机是否在工作
Video 是否在播放视频
Foreground process 前台进程
Package install 是否在进行包安装
Package active 包管理在工作
Battery level 电池当前电量
Temperature 电池温度
Charging on 在充电
Logcat misc 是否在导出日志
往下拉,会有一个 App stats,这里有两个信息比较重要,一个是 Device estimated power use,表示这段时间内,当前应用占系统的耗电百分比为4.61%。一个是 Foreground,表示这段时间内,当前应用在前台的时间。(这里是一启动,就直接被退出,在后台运行,所以只有 4 秒多)
下方的 Network Information、Wakelocks、Service、Progress info 里面有详细的各个模块信息,也可以进行查看。
前面提到过,对于电量的软件测试我们没有很好的方案,只能提几个注意点:
尽量减少后台任务的执行,如果应用在后台执行时候,如果有后台任务,这时候应用还是会耗电的。
延迟一些事件,当设备充电/wifi 时候再去执行,如果需求允许的话,对处理要求及时性不是很高,可以延长任务的执行,等待充电的时候进行执行(这时候就不怕掉电),或者在 wifi 的时候(wifi 比蜂窝网络耗电低)。
判断是否充电:
充电状态可以使用一个 Receiver 进行接收广播。
/**
* Created by Administrator on 2018/1/15 0015.
* 被动接收充电状态
*/
public class PowerConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(action,Intent.ACTION_POWER_CONNECTED)){
Toast.makeText(context,"当前正在充电",Toast.LENGTH_SHORT).show();
} else if(TextUtils.equals(action,Intent.ACTION_POWER_DISCONNECTED)){
Toast.makeText(context,"当前不在充电",Toast.LENGTH_SHORT).show();
}
}
}
这是被动的通过系统的广播进行获取到系统的充电状态,也可以主动的去进行查询。
/**
* 是否正在充电
* @return
*/
public static boolean isPlugged(Context context){
//发送个包含充电状态的广播,并且是一个持续的广播
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = context.registerReceiver(null, filter);
//获取充电状态
int isPlugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
//充电器充电
boolean acPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_AC;
//USB 充电
boolean usbPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_USB;
//无线充电
boolean wifiPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
return acPlugged || usbPlugged || wifiPlugged;
}
判断是否使用 Wifi:
网络状态也可以通过使用 Receiver 进行接收系统的广播。
/**
* Created by Administrator on 2018/1/15 0015.
* 被动获取是否wifi
*/
public class WifiConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Battery.isWifi(context)){
Log.i("WIFI","当前正在使用wifi");
Toast.makeText(context,"当前正在使用wifi",Toast.LENGTH_SHORT).show();
} else{
Log.i("WIFI","当前不正在使用wifi");
Toast.makeText(context,"当前不在使用wifi",Toast.LENGTH_SHORT).show();
}
}
}
同样的,也可以我们主动去获取网络状态。
/**
* 是否正在使用wifi
* @param context
* @return
*/
public static boolean isWifi(Context context){
ConnectivityManager cm = (ConnectivityManager) context.
getSystemService(Context.CONNECTIVITY_SERVICE);
//获得当前活动的网络信息
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
if (null != activeNetworkInfo && activeNetworkInfo.isConnected() &&
activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI){
return true;
}
return false;
}
对于程序的多个处理任务,等待一定数量或时间,然后进行同时执行,可以避免多次唤醒 CPU。
根据情况可以使用 Jobscheduler/Alarm 替代。 Wakelock 会导致 CPU 一直处于运行的状态,无法得到休息。
WakeLock级别:
PARTIAL_WAKE_LOCK:保证 CPU 保持高性能运行,而屏幕和键盘背光(也可能是触摸按键的背光)关闭。一般情况下都会使用这个WakeLock。
ACQUIRE_CAUSES_WAKEUP:这个 WakeLock 除了会使 CPU 高性能运行外还会导致屏幕亮起,即使屏幕原先处于关闭的状态下。
ON_AFTER_RELEASE:如果释放 WakeLock 的时候屏幕处于亮着的状态,则在释放 WakeLock 之后让屏幕再保持亮一小会。如果释放 WakeLock 的时候屏幕本身就没亮,则不会有动作。
API17 被弃用的 WakeLock:保持屏幕长亮。
SCREEN_DIM_WAKE_LOCK:保证屏幕亮起,但是亮度可能比较低。同时键盘背光也可以不亮。
SCREEN_BRIGHT_WAKE_LOCK:保证屏幕全亮,同时键盘背光也亮。
FULL_WAKE_LOCK:表现和 SCREEN_BRIGHT_WAKE_LOCK 类似,但是区别在于这个等级的 WakeLock 使用的是最高亮度!
推荐是用 WindowFlagWindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON。使用方法是:
在Activity中: getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)。
在布局中添加这个属性:android:keepScreenOn="true"。
及时关闭掉不再需要的功能,如 GPS、网络请求超时设置。
为了更好的优化电量,从 Android 6.0(API 23)开始,Android 引入了两个省电功能,可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。
低电耗模式通过在设备长时间处于闲置状态时推迟应用的后台 CPU 和网络 Activity 来减少电池消耗。
应用待机模式可推迟用户近期未与之交互的应用的后台网络 Activity。
低电耗模式和应用待机模式管理在 Android 6.0 或更高版本上运行的所有应用的行为,无论它们是否特别针对 API 级别 23。
注:官网已经写得十分详细,想详细了解的看官网介绍,直接上链接,中文版的。安卓官网中文链接
淡橙色表示 CPU 在运行,明显可以看出进入Doze 模式后,CPU 一段时间才会唤醒一次,且间隔越来越大。
**用户设备未插接电源、处于静止状态一段时间且屏幕关闭,设备会进入低电耗模式。**未插接电源与屏幕关闭比较好满足,静止一段时间,这个一段时间不一定,所以测试时候不好满足,可以使用命令行直接进入。
测试低电耗模式:
1.使用 Android 6.0(API 级别 23)或更高版本的系统映像配置硬件设备或虚拟设备。
2.将设备连接到开发计算机并安装应用
3.运行应用并使其保持活动状态
4.关闭设备屏幕。(应用保持活动状态。)
5.通过运行以下命令强制系统在低电耗模式之间循环切换:
$ adb shell dumpsys battery unplug
$ adb shell dumpsys deviceidle step
您可能需要多次运行第二个命令。不断地重复,直到设备变为空闲状态。
6.在重新激活设备后观察应用的行为。确保应用在设备退出低电耗模式时正常恢复。
在低电耗模式下,您的应用会受到以下限制:
暂停访问网络。
系统将忽略 wake locks。
标准 AlarmManager 闹铃(包括 setExact() 和 setWindow())推迟到下一维护时段。
如果您需要设置在低电耗模式下触发的闹铃,请使用 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()。
一般情况下,使用 setAlarmClock() 设置的闹铃将继续触发 — 但系统会在这些闹铃触发之前不久退出低电耗模式。
系统不执行 Wi-Fi 扫描。
系统不允许运行同步适配器。
系统不允许运行 JobScheduler。
应用待机模式允许系统判定应用在用户未主动使用它时处于空闲状态。 当用户有一段时间未触摸应用时,系统便会作出此判定,以下条件均不适用:
用户显式启动应用。
应用当前有一个进程位于前台(表现为 Activity 或前台服务形式,或被另一 Activity 或前台服务占用)。
应用生成用户可在锁屏或通知托盘中看到的通知。
当用户将设备插入电源时,系统将从待机状态释放应用,从而让它们可以自由访问网络并执行任何待定作业和同步。 如果设备长时间处于空闲状态,系统将按每天大约一次的频率允许空闲应用访问网络。
如果说低电耗模式或应用待机模式破坏了应用的核心功能,或者由于技术方面的原因而导致您的应用无法使用 GCM 高优先级消息,那么我们可以申请添加到白名单中,来豁免这种机制。
详细内容查看官网。安卓官网中文链接