Android优化(三)_延迟电池续航时间

性能优化总纲

大概会花一个月左右的时间出7-8个专题来分享一下在工做和学习中积累下来的Android性能优化经验。html

但愿你们会持续关注。python

如今是专题三:电池电量优化android

但这也仅仅是为你们提供一些思路与较为全面的总结,算不上什么,但愿有错误或问题在下面评论。算法

也欢迎点开个人掘金、简书、CSDN主页看看其余文章。shell

最后完结之后会将思惟导图与优化框架整理出来,请期待。性能优化

题记

电池虽小,地位却很是重要。移动设备使用电池,作任何事情都要费电。而大多数状况下,白天不多有机会给电池充电,哪怕你带了电宝,也可能出现不够用的状况。服务器

而做为开发者,若是你的程序被用户发现耗电量过多很容易被卸载,不再用,是很是致命的,所以咱们要制定一系列解决方案,防止此类事情发生。网络

本章带领你们探讨如何测量电池的使用量,以及便可以省电,又不影响用户体验的方法。app


1、电池

通常来讲,充满电的状态能够保证手机正常使用1-2天,除去屏幕和CPU所消耗的电量之外,设备使用多少电量严重以来全部应用都作了什么,也就是取决于你的应用是如何设计和实现的。 通常有以下功能:框架

  • 执行代码(显而易见)
  • 数据传输(上传和下载,使用WiFi,2G,3G,4G)
  • 定位
  • 传感器
  • 渲染图像
  • 唤醒任务 在学习如何最大限度的减小耗电量以前,咱们想要有办法来测量。

测量电池用量

一、Battery Historian

Battery Historian是Android 5.0开始引入的新API。经过下面的指令,能够获得设备上的电量消耗信息:

$ adb shell dumpsys batterystats > xxx.txt  //获得整个设备的电量消耗信息
$ adb shell dumpsys batterystats > com.package.name > xxx.txt //获得指定app相关的电量消耗信息复制代码

获得了原始的电量消耗数据以后,咱们须要经过Google编写的一个python脚本把数据信息转换成可读性更好的html文件:

$ python historian.py xxx.txt > xxx.html复制代码

打开这个转换事后的html文件,能够看到相似TraceView生成的列表数据,这里的数据信息量很大,这里就不展开了。

3) Track Battery Status & Battery Manager

咱们能够经过下面的代码来获取手机的当前充电状态:

IntentFilter filter=new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus=this.registerReceiver(null,filter);
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
boolean acCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_AC);
if(acCharge){
    Log.v(LOG_TAG,“Thephoneischarging!”);
}复制代码

在上面的例子演示了如何当即获取到手机的充电状态,获得充电状态信息以后,咱们能够有针对性的对部分代码作优化。好比咱们能够判断只有当前手机为AC充电状态时 才去执行一些很是耗电的操做。

private boolean checkForPower(){

IntentFilter filter=new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus=this.registerReceiver(null,filter);
int chargePlug  = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
boolean usbCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_USB);
boolean acCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_AC);
boolean wirelessCharge=false;
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN_MR1){
    wirelessCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_WIRELESS);
}
    return(usbCharge||acCharge||wirelessCharge);
}复制代码

二、另外一种方法获得耗电量

APP获取电量算法。 通过查看源码,咱们看到app计算电量的算法以下:

  • 在主Activity里面 info.getBatteryStats() 就搞定了。 首先 load(),若是load失败,走CPU时间计算,经过getAppListCpuTime这样函数。 CPU的时间计算,有3个核心步骤:
  1. ActivityManager遍历runningApp进程,获取对应pid
  2. getAppProcessTime(pid)经过读取/proc/pid/stat文件,拿取APP在CPU的运行时间。
  3. 从新为BatterySipper附值:+time;
  • 获取APP消耗 processAppUsage();也分三步走:
  1. 经过PowerProfile 获取cpu的速度层次(speedsteps),方便后面使用
  2. 根据不一样CPU的速度等级,计算cpu在某个速度下的电量,mA毫安
  3. mPowerProfile.getAveragePower(PowerProfile.POWERCPUACTIVE,p)

不少地方都用到这个API获取power。 那它究竟作了些什么呢? 查看系统源码能够知道:

实际上这句话是获取1个叫PowerMap的数据结果,得到电量。而PoweMap的赋值,是来源于com.android.internal.R.xml.powerprofile 的文件。 关于该文件的获取 android-版本号/core/res/res/xml/powerprofile.xml

计算各类耗电量的详细算法是: 来自深刻浅出Android App耗电量统计 总结App耗电量计算公式:(

Uid_Power(App耗电量,单位:mAh) = Uid_Power1 + Uid_Power2 + Uid_Power3 + Uid_Power4 + Uid_Power5

Uid_Power1 = (Process1_Power + … + ProcessN_Power);

Process_Power = (CPUSpeed_Time * POWER_CPU_ACTIVE);

Uid_Power2 = PartialWakeLock_Time * POWER_CPU_WAKE

Uid_Power3 = ( tcpBytesReceived + tcpBytesSent ) * averageCostPerByte

Uid_Power4 = wifiRunningTimeMs * POWER_WIFI_ON

Uid_Power5 = (Sensor1_Power + … + SensorN_Power)

Sensor_Power = Sensor_Time * Power_Sensor

2、禁用电池广播

系统除了定义了ACTION_BATTERY_CHANGED包含了电池信息,还定义了应用可使用的4个广播Intent:

  • ACTION_BATTERY_LOW
  • ACTION_BATTERY_OKAY
  • ACTION_POWER_CONNECRED
  • ACTION_POWER_DISCONNECTED

当咱们在一个广播接收器接收系统发送的这四个广播时,只要有一个发生,应用就会启动。这样有一个严重的缺陷,若是你在前台运行时,是没有问题的,可是若是在后台时,还出现Toast消息(非系统提醒),就有可能干扰其余应用,损害用户体验

因此咱们有一个很好的解决:

  • 只有当应用在前台运行时才能够启用广播
  • 步骤: 一、广播接收器默认必须是禁用 二、广播接收器必须在onResume()中启用,在onPause()被禁用

3、控制网络

如今基本全部的应用都必须在设备和服务器之间传递数据,就像获取电池状态同样,应用须要获取设备商的网络连接信息。ConnectivityManager类提供了API。供应用调用以此访问网络信息。 Android设备一般由多个数据链接:

  • Bluetooth
  • Ethernet
  • Wi_Fi
  • WiMax
  • 移动网络(EDGE、UMTS、LTE)

为了最大限度延长电池的使用时间,咱们须要直到以下事情:

  • 后台数据设置
  • 数据传输频度

后台数据

能够经过下面这个方法来获取后台数据的设置,不过在4.0之后,这个方法始终返回为true,当强行不容许时,网络会断开。

ConnectivityManager的getBackgroundDataSetting()复制代码

数据传输

传输速率的差别很是大,从小于每秒100Kb的GPRS数据链接到每秒几Mb的LTE或Wifi都有。除了链接类型,NetWorkInfo类还指定了链接的子类型,例如:

  • NETWORK_TYPE_GPRS(API 1)

  • NETWORK_TYPE_LTE(API 11)

  • NETWORK_TYPE_HSPAP(API 13)

若是建立和部署了新技术,也会增长新的子类型。留意一下每一个SDK版本的改动。

人们都习惯更快的链接,即便WiFi芯片耗电量比较多,但Wifi的速率和免费可让数据在最短期最小成本完成传输,从而下降电池消耗。

若是能控制数据的传输类型,就能够现压缩数据,再传输到设备上。虽然解压缩数据耗费CPU,也多用了些电量,但传输速度大大加快,数据通信设备能够很快关闭,从而延长了电池寿命。一般咱们这么作:

  • 使用GZIP压缩文本数据,使用GZIPInputStream类访问数据;
  • 使用匹配设备分辨率的资源(好比:没必要为320480的屏幕下载19201080的图片)

4、定位

如今不少的App都会作一件事:获取你的定位信息。一些用户可能愿意牺牲电池寿命来频繁的更新位置,而其余人定远县志更新次数,以确保设备点亮不会很快消耗完,因此须要提供不一样的昔阳县来知足用户需求。

一、注销监听器 仍是和处理广播那样,在onPause()中调用removeUpdates()能够注销监听器。

二、而且能够用requestLocationUpdates()调整更新频率。选择合适的时间间隔和最小间隔距离能够适应不一样的场景。

三、经过选择不一样的位置服务来控制,以下:

  • GPS定位
  • WIFI定位
  • 基站定位
  • AGPS定位

这四种定位的概念想必大多数都知道了,不知道的戳这里


5、传感器

传感器是个颇有意思的东西,与定位服务有点相似:应用向特定的传感器注册监听器,得到更新通知。

也是能够经过下降通知频率来省点,因为,每一个设备不一样,应用能够测量这4种延迟通知的频率,选择兼顾用户体验和省点的那一个。另外一种策略是,当发现值不变化时,使用NORMAL或UI延迟,当发现有忽然变化的时候,且话到GAME或FASTEST延迟。以下:

  • SENSOR_DELAY_NORMAL
  • SENSOR_DELAY_UI
  • SENSOR_DELAY_GAME
  • SENSOR_DELAY_FASTEST

6、图形

应用花费了不少时间在屏幕上画东西,不管是使用GPU渲染的3D游戏仍是使用CPU的日历程序,都想只以最少的代价赖在屏幕上展现指望的结果,以延长电池寿命。

如前所述,CPU非全速时使用的电量相对少一些。现代的CPU使用动态调频喝点呀来节省电力和减小发热量。这两种技术一般一块儿使用,成为DVFS技术(动态电压和频率调整),Linux的内核、Android以及现代处理器都支持这种技术。

虽然你不能直接控制电压和频率,或将内部组建断电,但能够控制应用渲染的方式。流畅的帧速率仍是要达到的。虽然在Android上帧率有上线(每秒60帧),但优化渲染仍是有效果的。除了可能下降能耗,你可能够为后台运行的应用留出更多的空间,提供了更好的总体用户体验。

例如:好比咱们手机里的壁纸,能够调用onVisibilityChanged()方法。事实上,壁纸能够是不可见的。但很容易忘记,持续绘制壁纸会消耗不少的电量。


7、唤醒

有时候你得应用可能出于某种缘由,需不时的被唤醒,去执行一些操做。

可是不多应用真正须要到提醒时间去强行唤醒设备。固然闹钟这类程序会须要这种功能,但大可能是等到用户主动唤醒才工做。

多数状况下,应用须要将将来某一刻安排提醒到时,但对时间要求并非很严格。为此Android定义了AlarmManager.setInexactRepeating(),它的参数和其“兄弟”setPepeating()基本相同,这种题型更节能,系统也避免了出现没必要要的唤醒,Android定义了5个提醒间隔:

  • INTERVAL_FIFTEEN_MINUTES
  • INTERVAL_HALF_HOUR
  • INTERVAL_HOUR
  • INTERVAL_HALF_DAY
  • INTERVAL_DAY

最好的结果就是全部应用都使用这种提醒,而不用精确的触发提醒。为了尽量的节电,应用还可让用户配置提醒的调度,由于有人发现较长时间间隔并不会对用户体验有很差的影响。


8、WakeLock

有些时候,一些应用即便长时间不和设备交互,也要阻止进入休眠状态,来保持良好的用户体验,就好比在看视频的时候,这种状况下,CPU须要作视频解码,同时屏幕保持开启,让用户可以观看。此外,视频播放时屏幕不能变暗。

Android为这种状况设计了WakeLoac类

private void runInWakeLoac(Runnable runnable,int flags){
    PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLoac wl = pm.newWakeLock(flag,"My WakeLock");
    wl.acquire();
    runnable.run();
    wl.release();
}复制代码

有一点须要注意:须要WAKE_LOCK权限。

系统的行为取决于WakeLock对象建立时传入的flags:

  • PARTIAL_WAKE_LOCK(CPU开)
  • SCREEN_DIM_WAKE_LOCK(CPU开、暗色显示)
  • SCREEN_BRIGHT_WAKE_LOCK(CPU开、明亮显示)
  • FULL_WAKE_LOCK(CPU开、明亮显示、键盘开) 这些标记能够结合使用
  • ACQUIRE_CAUSES_WAKEUP(打开屏幕和键盘)
  • ON_AFTER_RELEASE(WakeLock是放后继续保持屏幕和键盘开启片刻) 特别重要的一点:必定要释放WakeLock,在退出和暂停的时候,不然可能一直显示,电量很快耗光。

预防问题

防止出现特殊的问题,建议使用带超时的WakeLoac.acquire()版本,它会在超时后自动释放。 另外:能够用setKeepScreenOn()方法控制是否要保持屏幕,只要可见的View指定了要保持屏幕,屏幕就会一直保留。

9、总结

用户不会注意到应用是否延长了电池寿命。可是若是不作任何处理,那就有可能被注意到了。由于单个应用耗电过多,用户几天仍是能够感觉出来的,用户到时候就会卸载用用,因此要对电量使用作一些优化处理,而且给用户配置选项的自由,来应对用户产生的各类需求。

相关文章
相关标签/搜索