对智能手机的续航能力敏感的Android用户来说,下面这条无疑是个消息。援引Android
Police报道在Android 6.0
Marshmallow兼容性定义文档中详细规定OEM厂商必须透明电池使用情况。
当前很多设备厂商设备的电池管理并不透明,
不允许智能手机用户查看所有的系统和应用状态,而在新版Android系统中谷歌督促所有OEM厂商必须在设备中提供电池状态。

性能优化总纲

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

希望大家会持续关注。

现在是专题三:电池电量优化
但这也仅仅是为大家提供一些思路与较为全面的总结,算不上什么,希望有错误或问题在下面评论。

最后完结以后会将思维导图与优化框架整理出来,请期待。

获得详细信息可访问:   Compatibility Definition Document (PDF)

题记

电池虽小,地位却非常重要。移动设备使用电池,做任何事情都要费电。而大多数情况下,白天很少有机会给电池充电,哪怕你带了电宝,也可能出现不够用的情况。而作为开发者,如果你的程序被用户发现耗电量过多很容易被卸载,再也不用,是非常致命的,因此我们要制定一系列解决方案,防止此类事情发生。本章带领大家探讨如何测量电池的使用量,以及即可以省电,又不影响用户体验的方法。


澳门新葡亰 1

澳门新葡亰,一、电池

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

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

在官方文档中写道:“向应用开发者提供更加精准的电量统计和电池消耗报告,丰富的工具来激励优化应用的耗电。”此外所有设备必须要实现能够最终硬件组件的电池使用情况并传输到专属的应用中,尤其需要部署实现:

测量电池用量###

1、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生成的列表数据,这里的数据信息量很大,这里就不展开了。

  1. 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);
}

– 必须提供每个组件的功率配置文件,在Android Open Source
Project网站上以书面的形式定义每个硬件元件的当前消耗值,和随着时间变化的耗电量曲线图。

2、另一种方法得到耗电量

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

– 必须以mAh为单位报告所有硬件耗电量。

二、禁用电池广播

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

  • ACTION_BATTERY_LOW
  • ACTION_BATTERY_OKAY
  • ACTION_POWER_CONNECRED
  • ACTION_POWER_DISCONNECTED

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

所以我们有一个很好的解决:

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

– 如果无法将硬件组件的耗电量传输给应用则应该归类于硬件本身

三、控制网络

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

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

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

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


必须报告每个进程UID的的CPU耗电量。Android开源项目需要部署uid_cputime内核模组来实现。

后台数据

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

 ConnectivityManager的getBackgroundDataSetting()
网站地图xml地图