电量统计原理以及电量优化实践

"
Free apps like Free Chess and Angry Birds spend under 25-35 percent of their energy on game play, but over 65-75 percent on user tracking, uploading user information and downloading ads.
"
android电量统计的原理可以参看这篇文章:
http://duanqz.github.io/2015-07-21-batterystats-part1

大致原理摘录如下:
电量记录
1. Android在进行电量统计时,并不是采用直接记录电流消耗量的方式,而是跟踪硬件模块在不同状态下的使用时间,收集一些可用信息,用来近似的计算出电池消耗量。

举一个例子,假定某个APK的使用了GPS,使用时间用 t 表示。GPS模块单位时间的耗电量用 w 表示,那么,这个APK使用GPS的耗电量就可以按照如下方式计算:
耗电量 = 单位时间耗电量(w) × 使用时间(t)
frameworks.jar里的frameworks/base/core/res/res/xml/power_profile.xml这个文件,记录着各个模块单位时间的耗电量, 由厂商定义。
以下是Nexus 5(hammerhead)耗电参数配置的代码片段:


    
    0
    ...
    3.5
    73.24
    75.48
    ...
    2300

2. Android框架层通过一个名为batterystats的系统服务,实现了电量统计的功能。

收集信息被组织起来,在内存中的数据结构是由BatteryStats类描述的。 为了能够从不同维度统计耗电量,这个数据结构设计得比较复杂,我们不在这里展开讨论,仅通过一个收集应用程序前台运行时间的例子,来说明信息收集过程。
记录应用程序中所有Activity从显示状态(Resumed)到消失状态(Paused)的时间,就能够统计应用程序的前台运行时间。Activity状态的切换是由AMS掌控的,因此AMS需要将Activity的状态信息通知给batterystats服务。
当Activity要切换到显示状态(Resumed)时,
会调用ActivityStackSupervisor.resumeTopActivitiesLocked()方法,
接下来会调用ActivityStack.resumeTopActivityInnerLocked()方法来完成Activity的状态切换,在完成状态切换后, 会调用
ActivityStackSupervisor.reportResumedActivityLocked()方法,从这里开始,就开始通报了:“本Activity已经进入了显示状态”。
在ActivityStackSupervisor.reportResumedActivityLocked()中得到BatteryStatsImpl对象, 并启动一个计时器(StopwatchTimer),记录下了启动时间.在Activity pause时, 再得到结束时间, 这样就得到了应用程序的acitiviy在前台的运行时间了。

除了应用程序前台运行时间,还有很多信息是batterystats服务关注的,包括WakeLock、Sendor、Wifi、Audio、Video等,这些信息的采集方式与上述过程雷同,都会经过以下步骤:

  1. 由相应的模块发起状态变更的通知
  2. BatteryStats使用定时器记录起止时间
电量信息的储存

Android支持历史电量信息的显示的,如果重新启动Android,那内存中的数据就丢失了, 所以需要把这些信息存储到磁盘上,磁盘上的 /data/system/batterystats.bin 文件中就是电量信息的序列化数据。
batterystats服务启动时,会从 batterystats.bin 这个文件中读取数据,来初始化BatteryStats这个数据结构。

电量计算

BatteryStatsHelper.refreshStats()承载了电量计算的全部过程,在需要显示电量统计信息的地方,就可以通过BatteryStatsHelper这个类,来获取统计完成的电量信息。 Setting.apk就引用了这个类。电量计算大体可以分为两块:

1. AppUsage:应用程序耗电量计算,是指每一个应用程序使用硬件模块所产生的耗电量

在BatteryStatsHelper.processAppUsage()这个方法中,实现了应用程序的电量计算(实际上统计的粒度是uid,不同的apk可以运行在同一个uid)。

2. MiscUsage:其他杂项耗电量计算

所谓杂项,其实就是用户比较关心的一大类,包括:待机的耗电量、亮屏的耗电量、通话的耗电量、Wifi的耗电量等,这个统计是系统层面的, 作为app的开发人员可以忽略掉这部分内容。

我们来总结一下应用程序的电量计算过程。Android通过一个名为BatteryStats.Uid的数据结构来维护一个应用程序的电量统计信息。 这个数据结构中,又包含很多子结构:

Proc:表示属于Uid的进程,一个Uid中可能会有多个进程,每个进程都有CPU占用时间
WakeLock:表示Uid持有的WakeLock锁的电量统计,一个Uid也可能会持有多个锁
Mobile Radio:表示Uid使用数据流量的电量统计,譬如3G流量、4G流量
Wifi:表示Uid使用wifi的电量统计
Sendor:表示Uid使用传感器的电量统计

Android提供的dumpsys命令用于查看系统服务的信息, 将batterystats作为参数,就能输出完整的电量统计信息。

adb shell dumpsys batterystats
App电量优化实践
1. 打点网络优化

现在用户操作的打点信息是实时上报, 每单击一次菜单项就有大概1k的数据上传. 可以把这打点上传的执行封装起来, 间隔一段时间统一上传, 或是在特殊时间点统一上传, 可以有效减少频繁地使用wifi和mobile radio模块。

电量统计原理以及电量优化实践_第1张图片
dotting_network.png
TXD 发送数据 Transmit(tx) Data 的简写形式
RXD 接收数据 Receive(rx) Data 的简写形式
2. 尽量减少wakeLock的使用

例如现在的下载文件的逻辑, 应该使用带超时参数的acquire() API, 避免长时间使用wake lock.

DownloadThread.java
PowerManager.WakeLock wakeLock = null; 
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wakeLock.acquire();
3. 使用prepn profile 工具实时检测应用的耗电量
电量统计原理以及电量优化实践_第2张图片
trepn_profile.png

===DONE===

你可能感兴趣的:(电量统计原理以及电量优化实践)