电量优化原因
电量优化在 Android 开发中非常重要,原因如下:
用户体验:
电池续航时间是用户在使用移动设备时非常关注的因素之一。通过进行电量优化,可以延长设备的电池寿命,使用户能够更长时间地使用设备而不必频繁充电。这将极大地提升用户的满意度和整体体验。
设备性能:
移动设备的电池寿命与设备的性能直接相关。当设备的电池电量充足时,它可以提供更好的性能和响应速度。然而,在电池电量较低时,为了延长续航时间,系统可能会降低设备的性能,这会影响应用的运行效率和用户体验。通过进行电量优化,可以最大限度地减少系统对设备性能的调整,确保应用在各种电量状态下都能提供良好的性能。
资源利用:
电池是移动设备的重要资源之一,而且是有限的。通过进行电量优化,可以合理管理和利用电池资源,避免应用在后台消耗过多的电量,从而减少不必要的电能浪费。这对于设备的长时间使用和环境保护具有重要意义。
规范要求:
Android系统版本的更新通常会引入新的电量优化规则和限制,要求应用开发者遵守。例如,Android 6.0(Marshmallow)引入了Doze模式和应用待机模式,这些特性通过限制应用在后台的活动来减少耗电量。为了确保应用在新版本的系统上正常运行并符合规范,进行电量优化是必要的。
Doze低电耗模式和StandBy待机模式
当用户长时间未使用设备时,低电耗模式会延迟应用的后台 CPU 和网络活动,从 而降低耗电量。应用待机模式会延迟用户近期未与之交互的应用的后台网络活动
。
Doze低电耗模式
如果设备 未充电、屏幕熄灭、让设备在一段时间内保持不活动状态
,那么设备就会进入Doze模式。在Doze模式下,系统会尝试通过限制应用访问占用大量网络和 CPU 资源的服务
来节省电量。它还会阻止应用访问网络
,并延迟其作业、同步和标准闹钟
。
Doze中文是打盹,所以系统会定期退出
打盹一小段时间,让应用完成其延迟的活动。在此维护期内,系统会运行所有待处理的同步、作业和闹钟,并允许应用访问网络。
一旦用户通过移动设备、打开屏幕或连接至充电器唤醒设备,系统就会立即退出低电耗模式
,并且所有应用都会恢复正常活动。
在低电耗模式下,应用会受到以下限制:
1.暂停访问网络
。
2.系统忽略PowerManager.WakeLock
唤醒锁定。
3.标准 AlarmManager 闹钟
(包括 setExact() 和 setWindow() )推迟到下一个维护期
。
如果需要设置在设备处于低电耗模式时触发的闹钟,请使用API 23(6.0)提供的setAndAllowWhileIdle()
(一次性闹钟,同set方法)或 setExactAndAllowWhileIdle() (比set方法设
置的精度更高,同setExact)。
使用 setAlarmClock()
设置的闹钟将继续正常触发,系统会在这些闹钟触发之前不久退出低电耗模
式。
4.系统不执行 WLAN 扫描
。
5.系统不允许运行同步适配器AbstractThreadedSyncAdapter
(账号同步拉活)。
6.系统不允许运行 JobScheduler
。
Standby待机模式
应用待机模式允许系统判定应用在用户未主动使用它时是否处于待机状态
。当用户有一段时间未触摸应用并且应用没有以下表现,则Android系统就会使应用进入空闲状态
1.应用当前有一个进程在前台运行
(作为活动或前台服务,或者正在由其他活动或前台服务使用)。
2.应用生成用户可在锁定屏幕或通知栏中看到的通知
。
当用户将设备插入电源时,系统会从待机状态释放应用,允许它们自由访问网络并执行任何待处理的作业和同步。
如果设备长时间处于闲置状态,系统将允许闲置应用访问网络,频率大约每天一次。
白名单
系统提供了一个可配置的白名单,将部分免除低电耗模式和应用待机模式优化的应用列入其中。 在低电耗模式和应用待机模式期间,列入白名单的应用可以使用网络并保留部分唤醒锁定。不过,列入白名单的应用仍会受到其他限制,就像其他应用一样。例如,列入白名单的应用的作业和同步会延迟(在6.0及以下的设备上),并且其常规 AlarmManager 闹钟不会触发。应用可以调用 PowerManager.isIgnoringBatteryOptimizations()
来检查应用当前是否在豁免白名单中。
可以在设置 中的 电池优化手动配置白名单 。另外,系统也提供了一些方法,让应用要求用户将其列入白名单
应用可以触发 ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
Intent,让用户直接转到电池优化,以便他们在其中添加该应用。
startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
具有 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限的应用可以触发一个系统对话框,让用户直接将该应用添加到白名单,而无需转到“设置”。此类应用将通过触发ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
Intent 来触发该对话框。
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:"+getPackageName()));
startActivity(intent);
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
在低电耗模式下测试
#启用Doze
adb shell dumpsys deviceidle enable
#强制进入doze模式 (同时还需要关闭屏幕)
adb shell dumpsys deviceidle force-idle
#退出doze模式
adb shell dumpsys deviceidle unforce
#关闭doze
adb shell dumpsys deviceidle disable
#重置设备
adb shell dumpsys battery reset
#查看doze白名单
adb shell dumpsys deviceidle wh
在应用待机模式下测试
#设置断开充电
adb shell dumpsys battery unplug
#进入standby
adb shell am set-inactive <packageName> true
#退出standby
adb shell am set-inactive <packageName> false
#查看是否处于standby
adb shell am get-inactive <packageName>
#重置
adb shell dumpsys battery reset
监控电池电量和充电状态
获取充电状态
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = registerReceiver(null, ifilter);
// 是否正在充电
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// 什么方式充电?
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
//usb
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
//充电器
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
Log.e(TAG, "isCharging: " + isCharging + " usbCharge: " + usbCharge + " acCharge:" + acCharge);
监控充电状态变化
//注册广播
IntentFilter ifilter = new IntentFilter();
//充电状态
ifilter.addAction(Intent.ACTION_POWER_CONNECTED);
ifilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
//电量显著变化
ifilter.addAction(Intent.ACTION_BATTERY_LOW); //电量不足
ifilter.addAction(Intent.ACTION_BATTERY_OKAY); //电量从低变回高
powerConnectionReceiver = new PowerConnectionReceiver();
registerReceiver(powerConnectionReceiver, ifilter);
public class PowerConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Toast.makeText(context, "充电状态:CONNECTED", Toast.LENGTH_SHORT).show();
} else if (intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Toast.makeText(context, "充电状态:DISCONNECTED", Toast.LENGTH_SHORT).show();
} else if (intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
Toast.makeText(context, "电量过低", Toast.LENGTH_SHORT).show();
} else if (intent.getAction().equals(Intent.ACTION_BATTERY_OKAY)) {
Toast.makeText(context, "电量从低变回高", Toast.LENGTH_SHORT).show();
}
}
}
Energy Profiler
使用 Android 8.0 及以上版本的设备时,使用Energy Profiler 可以了解应用在哪里耗用了不必要的电量。 EnergyProfiler 会监控 CPU、网络无线装置和 GPS 传感器的使用情况,并直观地显示其中每个组件消耗的电量。还会显示可能会影响耗电量的系统事件(唤醒锁定、闹钟、作业和位置信息请求)的发生次数。