图1
图2
一、关于Doze模式
从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能:Doze模式(官方翻译为低电耗模式)和 App Standby模式(官方翻译为应用待机模式),可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。 Doze模式 通过在设备长时间处于闲置状态时推迟应用的后台 CPU 和网络 Activity 来减少电池消耗。 App Standby模式 可推迟用户近期未与之交互的应用的后台网络 Activity。
进入doze的条件
- 屏幕关闭
- 没有插USB(充电中)
- 手机处于静止状态一段时间
doze模式下应用受限功能
- 网络访问被禁用,但是一些高优先级的GCM推送消息将会被放行
- Wake locks被忽略
- Alarms被屏蔽,除非调用setAlarmClock()和AlarmManager.setAndAllowWhileIdle()
- WiFi热点扫描停止
- 同步和JobScheduler调度任务被挂起
白名单
类似的权限管理通常都会有白名单,doze也不例外,名单中的应用不受上述doze限制,例如系统自带的下载服务,Google Play及GMS服务都默认加入白名单。用户可以通过系统设置->应用->高级->忽略优化界面添加或移除白名单,如下图所示。
调试
使用adb命令可以手动将手机切入doze模式,即IDLE状态进行调试。
带USB调试的时候要先将充电模式禁止掉,使用battery服务的unplug命令。
顾名思义,dumpsys是用来查看系统service,而doze相关的系统服务是"deviceidle",如下命令即可查看。
新增加的“deviceidle”服务是通过IDeviceIdleController接口实现的,后面将会对其进行源码分析。 进入doze需要满足三方面的条件,这些条件控制着DeviceIdleController内部的状态机实现,分为5个状态:
- ACTIVE -手机亮屏使用或者充电中
- INACTIVE - 刚脱离ACTIVE状态(如手机关闭屏幕)
- IDEL_PENDING - 准备进入IDLE状态
- IDLE - 进入IDLE状态
- IDLEMANINTENANCE - IDLE状态保持一段时间后,短暂唤醒做一些事情
[注]Release 2在IDEL_PENDING和IDLE状态间增加了SENSING表示在进入IDLE侦测运动情况的状态。
我们可使用下面命令dump出手机当前的IDLE状态信息,包括白名单列表。
在禁用充电模式关闭屏幕后,手机会进入INACTIVE状态,此时通过step命令来手工控制状态切换。
也可以通过whitelist命令增加或删除白名单应用。
源码剖析
下面基于Android M Preview Release 1 版本对doze相关代码进行分析。系统在com/android/server路径下新增了一个继承自SystemService的DeviceIdleController服务类负责doze的控制逻辑。其内部字符串常量SERVICE_NAME明确定义了其服务的名称为“deviceidle”。
[注]Release 2该定义由Context.java新增常量DEVICE_IDLE_CONTROLLER取代。
状态机
系统定义了5个int常量表示5个状态。
具体源码分析参考《 Android M doze特性预研 》
二、判断Doze模式
我们判断Doze模式,Context虽然增加了deviceidle服务,同样也可以访问
Object obj = context.getSystemService("deviceidle");
obj是DeviceIdleManager的实例,该类持有mService ( IDeviceIdleController binder proxy),虽然以provided或compileOnly引入layoutlib.jar转换该类是可行的, 但是DeviceIdleManager类在framework中,部分过程需要反射进行,存在兼容性问题的可能。
如下两种方式都是比较好的方法
1、PowerManager中的isDeviceIdleMode方法
2、监听android.os.action.DEVICE_IDLE_MODE_CHANGED广播
三、解除Doze模式
最后探讨下应用如何“悄悄”地使系统退出doze模式。根据doze的条件,在没有充电的情况下,只能通过亮屏或震动等外部事件触发系统退出IDLE状态。
1. AlarmManager setAlarmClock()
这两个方法同样能短暂解除Doze,以此保证Doze模式下闹钟可以精准执行。
2.亮屏
APP拥有“android.permission.WAKE_LOCK”权限,执行下面代码即可点亮屏幕,实测可以让手机马上退出doze模式。
1.震动
DeviceIdleController使用的运动传感器是Significant motion,单触发低功耗的传感器,调用deviceidle dump命令即可查看实际传感器信息。