Doze模式的主要实现是在framework中的DeviceIdleController类:分为Deep Idle 和 Light Idle模式两种
Deep Idle模式
先说下用到的几个时间常量值(括号类的是原生的时间,右边的是项目中实际用到的值)
INACTIVE_TIMEOUT(30min/3min) 6min
Inactive状态的时长;
MOTION_INACTIVE_TIMEOUT(10min/1min) 1min
运动传感器监测改变之后,进入Inactive的持续时长。
IDLE_AFTER_INACTIVE_TIMEOUT(30min/3min) 3min
Pend状态的时长;
SENSING_TIMEOUT(4min/1min) 60s
Sensing状态持续时长;
LOCATING_TIMEOUT(30s/15s) 15s
Locating状态最多持续时长;
IDLE_TIMEOUT(60min/6min) 60min(实际每次都是60min)
Idle状态持续时长,可变。
MAX_IDLE_TIMEOUT(6h/30min) 30min
Idle状态持续时长最大值,但是持续时长最小不小于初始值。
IDLE_PENDING_TIMEOUT(5min/30s) 30s
Idle_Maintenance状态持续时长,可变;
MAX_IDLE_PENDING_TIMEOUT(10min/60s) 60s
Idle_Maintenance状态持续时长最大值,最小不小于初始值。
deep idle模式的7个状态:
1 Active
2 InActive
3 Idle_Pending
4 Sensing
5 Locating
6 Idle
7 Idle_Maintenance
下面分析各个状态
1 手机亮屏或者插上USB,都是处于Active状态
2 手机灭屏且没有连上USB,通过对Battery_Changed的广播接收和对Dispaly屏幕变化的监听最后都是调用becomeInactiveIfAppropriateLocked方法进入InActive状态。在这个方法中,先是将State置为InActive,然后调用resetIdleManagementLocked方法重置各种状态,包括重置Alarm,取消传感器,位置监测等。最后调用scheduleAlarmLocked方法设置一个Alarm定时一段时间执行stepIdleStateLocked方法。
3 当上一步的定时到了开始执行stepIdleStateLocked方法,进入stepIdleStateLocked判断,首先,startMonitoringMotionLocked()注册一个特殊运动传感器,监测手机有没有特殊移动,然后scheduleAlarmLocked设置Alarm一段时间后再次调用本方法,随后将State设为Idle_Pending状态,并将Idle和Idle_Maintenance的时间间隔设为初始值。
4当上一步的Alarm时间到了以后,回调stepIdleStateLocked方法,执行stepIdleStateLocked选择,首先将State置为Sensing状态。接着调用scheduleSensingTimeoutAlarmLocked方法设置Alarm定时调用becomeInactiveIfAppropriateLocked这个方法,取消位置监听;调用mAnyMotionDetector.checkForAnyMotion()方法注册加速度传感器,监测各个方向上有没有运动,之后通过mCallback.onAnyMotionResult(status)将结果返回给DeviceIdleControler,若没有运动,则调用stepIdleStateLocked进入下个一状态,否则调用handleMotionDetectedLocked进入InActive状态。
5 当上一步监测到没有运动时,调用stepIdleStateLocked方法,进入STATE_SENSING选择,首先,调用cancelSensingTimeoutAlarmLocked取消上一步的定时调用。接着将state置为Locating,然后设置一个Alarm定时调用本方法,接着,注册基于网络和基于GPS的位置监听,但是前提是打开了定位服务,并且GPS使用的是网络和GPS定位,若都没有满足,则直接进入下一个case状态。在两个位置监听中都是在LocationChanged方法中,若有一次位置精度小于一个值(20m),则再根据上一步的传感器监测判断位置有没有变换,没有则直接回调本方法进入下一个状态,否则就是等到设置的Alarm到了之后才回调本方法。
6 当上一步再次调用stepIdleStateLocked方法之后,进入Locating选择,取消定位服务监测,取消Alarm定时,取消加速度传感器监测,接着由于没有break,则直接进入STATE_IDLE_MAINTENANCE选择。在这个选择中,先设置一个定时的Alarm,这个Alarm是通过setIdleUntil方法设置,比较特殊,可以重置系统中其他Alarm。然后计算下一次的Idle时间,将state状态置为Idle状态,然后将LightState状态置为LIGHT_STATE_OVERRIDE,取消Light Idle模式的Alarm定时,最后通过Handler发送消息,通知各个服务进入Idle状态。
7 当设置的Alarm的Idle持续时间到了以后,调用stepIdleStateLocked方法,进入STATE_IDLE选择,首先唤醒系统,设置一个时间为 mNextIdlePendingDelay的Alarm定时回调本方法,然后重新计算下mNextIdlePendingDelay的值,将state状态置为Idle_Maintenance,然后通过Handler发送消息通知各服务退出Idle模式。
当mNextIdlePendingDelay时间到了以后再次调用本方法,进入Idle状态,依次循环。但是在此期间若有USB连接或者屏幕电量则退出当前模式,直接进入Active状态。
Light Idle模式:
LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT(5min/15s):5s
Light Idle模式从inactive到pre_idle状态的时间。
LIGHT_PRE_IDLE_TIMEOUT(10min/30s): 5s
pre_idle到idle状态的时间。
LIGHT_IDLE_TIMEOUT(5min/15s): 5min
Light idle持续的时长初始值,light idle模式的持续时长是会变的
LIGHT_MAX_IDLE_TIMEOUT(15min/60s): 60s
Light idle模式持续时长的最大值,但是持续时长最小不小于初始值
LIGHT_IDLE_FACTOR(2):
Light idle模式持续时长的比例因子,即没经过一次light idle模式持续时长都要乘以这个比例因子,但是最长不能超过最大持续时间。
LIGHT_IDLE_MAINTENANCE_MIN_BUDGET(1min/15s): 15s
Light idle maintenance的持续时间的最小值。
LIGHT_IDLE_MAINTENANCE_MAX_BUDGET(5min/30s): 30s
Light idle maintenance的持续时间的最大值。
Light Idle模式的状态
1 LIGHT_STATE_ACTIVE
2 LIGHT_STATE_INACTIVE
3 LIGHT_STATE_PRE_IDLE
4 LIGHT_STATE_IDLE
5 LIGHT_STATE_WAITING_FOR_NETWORK
6 LIGHT_STATE_IDLE_MAINTENANCE
7 LIGHT_STATE_OVERRIDE
下面分析各个状态
1 手机亮屏或者处于USB连接状态
2 手机灭屏并且没有USB连接,通过灭屏和Battery_Changed广播监听,将Light State状态置为LIGHT_STATE_INACTIVE,resetLightIdleManagementLocked重置各个状态,并设置(5min/15s)的Alarm,时间到了执行stepLightIdleStateLocked方法。
3 上一步的alarm到了,执行stepLightIdleStateLocked方法,进入LIGHT_STATE_INACTIVE选择,判断当前系统是否有ops执行,若有,则将状态置为LIGHT_STATE_PRE_IDLE,继续执行任务,并设置(10min/30s)的Alarm继续执行stepLightIdleStateLocked方法。若没有ops执行,则直接进入LIGHT_STATE_IDLE模式。
4 上一步的Alarm到了之后,执行stepLightIdleStateLocked方法,将Light State置为LIGHT_STATE_IDLE,并设置(5min/15s,可变)的Alarm调用本方法,发送消息执行进入Light Idle模式的操作。
5 上一步Alarm到了以后,执行stepLightIdleStateLocked方法,首先判断是否有网络连接。若没有网络连接,则将状态置为LIGHT_STATE_WAITING_FOR_NETWORK,设置一个Light Idle时间的Alarm等待网络连接。若有网络连接,则将Light State置为LIGHT_STATE_IDLE_MAINTENANCE,执行挂起操作,设置一个定时Alarm。
6 上一步的的判断中若没有网络连接,等待时间到了以后,将状态置为LIGHT_STATE_IDLE_MAINTENANCE,接着执行挂起操作,设置一个定时Alarm。当这个Alarm到了以后,将状态置为LIGHT_STATE_IDLE,依次循环。
7 在进入deep idle的之后,会将Light State的状态置为LIGHT_STATE_OVERRIDE,这样每次调用stepLightIdleStateLocked都是直接返回,而不执行任何操作,即进入Deep Idle之后就取消Light Idle的一切操作。
从目前的feature来看,进入Light Idle之后,网络会和deep dle模式一样中断,但是Alarm不会重置,PowerManager对wake lock的操作不一样,别的服务通过发送 不同的广播,操作也应该是不一样的。
对于进入Idle模式后,app应该怎么做?
1 考虑引导用户将app加入白名单,即电池不优化名单
2 Idle模式会忽略wake lock并且设置的Alarm也会被重置,但AlarmManger提供方法setAndAllowWhileIdle和setExactAndAllowWhileIdle,即使在Idle模式下该Alarm也可以运行。setAlarmClock设定的Alarm也能够运行。
对于网络服务,若灭屏情况下应用优先级仍然很高(<=4 前台服务,这个优先级是在AlarmManager里面定义的Process_State的优先级),则即使不在白名单里面,也是不会禁止网络连接的。但是实测百度浏览器下载的时候,浏览器优先级为2,一旦灭屏就变成了5,然后网络就被禁止了。灭屏情况下优先级<=4的有图库,谷歌搜索。。。。。
设置里面的电池优化选项中,将app置为未优化,就是在doze模式下允许运行,最终是调用到DeviceIdleControler中的addPowerSaveWhitelistAppInternal方法,将应用加入到mPowerSaveWhitelistUserApps这个集合中去了。并且会将改应用写入到配置文件deviceidle.xml中去,方便下次读取。该文件目录为data/system/deviceidle.xml。
doze模式的白名单还可以通过framework/base/data/etc/platform.xml添加标记为allow-in-power-save的项设置。在编译时期添加白名单。
最后来张图片,是Deep Idle模式的,Liight Idle模式类似。点击查看大图