微信公众号

Android 电量优化

为什么优化

现在智能手机基本能一天一充,如果一个应用耗电过多,肯定是有问题的,而开发中电量的优化可能是最容易被忽略的。

如何检测

1、手机设置-电池使用情况,查看电量消耗;

2、使用 Battery Historian 配置电池使用情况。

优化方案

监控充电状态和电池电量

确定当前充电状态

1
2
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);

简单地调用 registerReceiver 传入 null 作为接收器来注册 BroadcastReceiver,便可返回当前电池状态 Intent。

1
2
3
4
5
6
7
8
9
10
11
12
// 充电状态
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
Log.d("wxl", "isCharging=" + isCharging);
Toast.makeText(this,"isCharging=" + isCharging,Toast.LENGTH_LONG).show();
// 设备是通过 USB 还是交流充电器进行充电
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
Log.d("wxl", "usbCharge=" + usbCharge + ",acCharge=" + acCharge);
Toast.makeText(this,"usbCharge=" + usbCharge + ",acCharge=" + acCharge,Toast.LENGTH_LONG).show();

监控充电状态变化

清单文件中注册一个 BroadcastReceiver,通过在一个 Intent 过滤器内定义 ACTION_POWER_CONNECTED 和 ACTION_POWER_DISCONNECTED 来同时侦听这两种事件。

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>

PowerConnectionReceiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class PowerConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
String batteryStatus = "";
switch (status) {
case BatteryManager.BATTERY_STATUS_CHARGING:
batteryStatus = "正在充电";
break;
case BatteryManager.BATTERY_STATUS_DISCHARGING:
batteryStatus = "正在放电";
break;
case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
batteryStatus = "未充电";
break;
case BatteryManager.BATTERY_STATUS_FULL:
batteryStatus = "充满电";
break;
case BatteryManager.BATTERY_STATUS_UNKNOWN:
batteryStatus = "未知道状态";
break;
}
Toast.makeText(context, "batteryStatus=" + batteryStatus, Toast.LENGTH_LONG).show();
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_AC);
String chargePlug = "";
switch (plugged) {
case BatteryManager.BATTERY_PLUGGED_AC:
chargePlug = "AC充电";
break;
case BatteryManager.BATTERY_PLUGGED_USB:
chargePlug = "USB充电";
break;
case BatteryManager.BATTERY_PLUGGED_WIRELESS:
chargePlug = "无线充电";
break;
}
Toast.makeText(context, "chargePlug=" + chargePlug, Toast.LENGTH_LONG).show();
}
}

注册

1
2
3
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
this.registerReceiver(new PowerConnectionReceiver(), intentFilter);

监控电池电量变化

清单文件中注册一个 BroadcastReceiver,通过侦听 ACTION_BATTERY_LOW 和 ACTION_BATTERY_OKAY,每当设备电池电量不足或退出不足状态时,便会触发该接收器。

1
2
3
4
5
6
<receiver android:name=".BatteryLevelReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
<action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
</intent-filter>
</receiver>

BatteryLevelReceiver

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BatteryLevelReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//当前剩余电量
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
//电量最大值
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
//电量百分比
float batteryPct = level / (float) scale;
Log.d("wxl", "batteryPct=" + batteryPct);
Toast.makeText(context, "batteryPct=" + batteryPct, Toast.LENGTH_LONG).show();
}
}

注册

1
2
3
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
this.registerReceiver(new BatteryLevelReceiver(), intentFilter);

通常,如果设备连接了交流充电器,您应该最大限度提高后台更新的频率;而如果设备是通过 USB 充电,则应降低更新频率,如果电池正在放电,则应进一步降低更新频率;在电池电量极低时停用所有后台更新。

屏幕保持常亮

当设备从休眠状态中,被应用程序唤醒一瞬间会耗电过多,我们可以保持屏幕常亮来节省电量,代码声明:

1
2
3
4
//屏幕保持常亮
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//一般不需要人为的去掉 FLAG_KEEP_SCREEN_ON 的 flag,windowManager 会管理好程序进入后台回到前台的的操作
//getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

或者,直接在布局中加上 keepScreenOn=true :

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
tools:context="com.wuxiaolong.batterysample.MainActivity">
</android.support.constraint.ConstraintLayout>

JobScheduler 使用

需要在稍后的某个时间点或者当满足某个特定的条件时执行一个任务,例如当设备接通电源适配器或者连接到 WIFI,在 API 21,google 提供了一个新叫做 JobScheduler API 的组件来处理这样的场景。JobScheduler API 允许同时执行多个任务,执行某些指定的任务时不需要考虑时机控制引起的电池消耗。
官方 demo 地址:https://github.com/googlesamples/android-JobScheduler

低电耗模式和应用待机模式

从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能,可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。低电耗模式通过在设备长时间处于闲置状态时推迟应用的后台 CPU 和网络 Activity 来减少电池消耗。应用待机模式可推迟用户近期未与之交互的应用的后台网络 Activity。

低电耗模式

如果用户设备未插接电源、处于静止状态一段时间且屏幕关闭,设备会进入低电耗模式。 在低电耗模式下,系统会尝试通过限制应用对网络和 CPU 密集型服务的访问来节省电量。这还可以阻止应用访问网络并推迟其作业、同步和标准闹铃。

在低电耗模式下,您的应用会受到以下限制:

  • 暂停访问网络。
  • 系统将忽略 wake locks。
  • 标准 AlarmManager 闹铃(包括 setExact() 和 setWindow())推迟到下一维护时段。
    • 如果您需要设置在低电耗模式下触发的闹铃,请使用 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()。
    • 一般情况下,使用 setAlarmClock() 设置的闹铃将继续触发 — 但系统会在这些闹铃触发之前不久退出低电耗模式。
  • 系统不执行 Wi-Fi 扫描。
  • 系统不允许运行同步适配器。
  • 系统不允许运行 JobScheduler。

低电耗模式很容易影响 AlarmManager 闹铃和定时器管理的 Activity,因为当系统处于低电耗模式时,不会触发 Android 5.1(API 级别 22)或更低版本中的闹铃。

为了帮助您安排闹铃,Android 6.0(API 级别 23)引入了两种新的 AlarmManager 方法:setAndAllowWhileIdle() 和 setExactAndAllowWhileIdle()。通过这些方法,您可以设置即使设备处于低电耗模式也会触发的闹铃。

应用待机模式

应用待机模式允许系统判定应用在用户未主动使用它时处于空闲状态。 当用户有一段时间未触摸应用时,系统便会作出此判定,以下条件均不适用:

  • 用户显式启动应用。
  • 应用当前有一个进程位于前台(表现为 Activity 或前台服务形式,或被另一 Activity 或前台服务占用)。
  • 应用生成用户可在锁屏或通知托盘中看到的通知。

当用户将设备插入电源时,系统将从待机状态释放应用,从而让它们可以自由访问网络并执行任何待定作业和同步。 如果设备长时间处于空闲状态,系统将按每天大约一次的频率允许空闲应用访问网络。

参考

https://developer.android.google.cn/training/monitoring-device-state/index.html