一. 前言

​ 耗电情况,例如:打开屏幕,所有要使用CPU/GPU工作的动作都会唤醒屏幕,都会消耗电量。这和应用程序唤醒设备还不一样。比如使用叫醒闹钟(wake clock)、AlarmManager、JobSchedulerAPI。

二. 电量消耗情景

1. 移动网络请求

当设备通过无线网发送数据的时候,为了使用硬件,这里会出现一个唤醒耗电高峰。接下来还 有一个高数值,这是发送数据包消耗的电量,然后接受数据包也会消耗大量电量 也看到一个峰值。

2. WakeLock

Android 系统本身为了优化电量的使用,会在没有操作时进入休眠状态,来节省电量。当然,为了便于开发(很多应用不可避免的希望在灭屏后还能运行一些事儿,或是要保持屏幕一直亮着–比如播放视频),Android 提供了一个 PowerManager.WakeLock 的东西。

我们可以用 WakeLock 来保持 CPU 运行,或是防止屏幕变暗/关闭,让手机可以在用户不操作时依然可以做一些事儿。然而,获取 WakeLock 很容易,释放不好就会成为难题,消耗电量。例如获取了一个 WakeLock 来保持 CPU 运转,做一个复杂运算并将数据上传到后台服务器,然后释放该 WakeLock。然而这个过程可能并不像我们想象的那么快,可能因为比如服务器挂掉,计算出了异常等等导致 WakeLock 没有释放,CPU 会一直得不到休眠,而大大增加耗电。

3. GPS

应用中经常会用到定位服务,Android 提供了 Network 定位和 GPS 定位。相对来说,GPS 会精确得多,对于一些诸如跑步,导航类的应用基本会使用 GPS 定位。然而,GPS 定位也会消耗大量的电量。

二. 电量分析工具

Battery History 工具

要进行电量优化,我们首先得知道电都消耗到哪里去了,我们可以通过 google 开源的 Battery-Historian 来进行分析。

工具开源地址: https://github.com/google/battery-historian

三. 优化措施

1. 优化网络请求

在蜂窝移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求。

尽量在 Wi-Fi 环境下使用数据传输。

2. 谨慎使用WakeLock

WakeLock 获取释放成对出现(调用 release),使用超时 WakeLock,以防出异常导致没有释放。

WakeLock 有一个接口 setReferenceCounted,用来设置 WakeLock 的计数机制,true 为计数,false 为不计数,默认是 true。所谓计数即每一个 acquire 必须对应一个 release;不计数则是无论有多少个 acquire,一个 release 就可以释放。虽然官方说默认 是计数的,但有的第三方 ROM 做了修改,使默认是不计数的。

主动设置 wakeLock.setReferenceCounted(false)

3. 监听手机充电状态

BatteryManager 会发送一个包含充电状态的持续广播,我们可以通过此广播获取充电状态和电量详情。因为这是一个持续广播,无需写 Receiver,可以直接通过 intent 获取相关数据。

1
2
3
4
5
6
7
8
9
10
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null,ifilter);
// 设备正在充电
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS,-1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// 也可以监听充电状态的变化,只要设备连接或断开电源,BatteryManager 就会广播相应的操作
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;

另外页可以注册 Receiver来监听

1
2
3
4
5
6
<receiver android:name=".PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>

4. Doze and App Standby

Android 6.0 提供了两个用来节省电量的技术 Doze 和 App Standby。

  • Doze 瞌睡。如果设备闲置了一段较长时间,Doze 技术将通过延迟后台网络活动,CPU 运行等来减少电量损耗。
  • App Standy 应用待机。不是最近得到过用户使用的 App,App Standy 将延缓这个应用的后台网络活动。

所有 Android 6.0 及以上的设备上,Doze and App Standby 都会运行。可能会影响 App 的运行,可以根据官方文档适配。

可以在代码中调起电量优化的设计页面,让用户选择是否将应用加入白名单,以在 Doze 模式下能够做一些事情。

5. 定位中使用 GPS,及时关闭

1
2
// Remove the listener you previously added
locationManager.removeUpdates(locationListener);

6. 计算优化

缩短代码产生指令运行的时间,进而减少某个应用程序对 CPU 时间片 的总占用时间,进而减少单位时间内该应用程序占整个系统耗电的百分比。

浮点运算比整数运算更消耗 CPU 时间片,因此耗电也会增加,在编写 代码的过程中应该尽量减少浮点运算。

  • 除法变乘法。
  • 充分利用移位。
  • 查表法,直接使用映射关系,但这会增加内存占用,视情况而定。

7. 其他

熄屏后停止一些和 UI 效果有关的操作,比如动画。

参考文章

Android 优化——电量优化

Android电量优化全解析