概述

LightDoze表示轻度doze模式,如果设备处于未充电且屏幕关闭状态,但未处于静止状态时,就会进入Light Doze模式,在LightDoze模式中,会定期进行维护,这种维护会持续N分钟,在维护状态(maintenance)时,会进行网络的访问,和同步、JobScheduler的操作,然后又会进入Idle状态,持续多次。之后如果设备仍旧保持静止,则会进入Deep Doze模式,因此,如果没有运动传感器,则无法检测设备是否处于静止,因此无法进入Deep Doze模式。

原理

在DeviceIdleController(以下简称DIC)中,定义了几个用来表示Light Doze模式有关的值,这些值及其意义如下:


//表示轻度doze模式
private int mLightState;
//mLightState状态值,表示设备处于活动状态
private static final int LIGHT_STATE_ACTIVE = 0;
//mLightState状态值,表示设备处于不活动状态
private static final int LIGHT_STATE_INACTIVE = 1;
//mLightState状态值,表示设备进入空闲状态前,需要等待完成必要操作
private static final int LIGHT_STATE_PRE_IDLE = 3;
//mLightState状态值,表示设备处于空闲状态,该状态内将进行优化
private static final int LIGHT_STATE_IDLE = 4;
//mLightState状态值,表示设备处于空闲状态,要进入维护状态,先等待网络连接
private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
//mLightState状态值,表示设备处于维护状态
private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
//mLightState状态值,表示设备轻度Doze被覆盖,开始进入深度Doze模式
private static final int LIGHT_STATE_OVERRIDE = 7;

从上述代码中可知,共有7个状态类表示Light Doze模式的不同状态。接下来开始分析Light Doze的实现以及其七个状态之间的转换。

在第一部分中分析启动流程时,当DCI启动流程的最后,会调用becomeInactiveIfAppropriateLocked()方法和becomeActiveLocked()方法,这两个方法用来切换交互/不可交互状态,这里先看第一个方法:

void becomeInactiveIfAppropriateLocked() {//仅当屏幕灭屏且没充电时,才能进入Doze模式,mForceIdle是shell设置相关if ((!mScreenOn && !mCharging) || mForceIdle) {//处理深度Doze,该状态表示当前设备处于交互状态,即亮屏、有动作移动if (mState == STATE_ACTIVE && mDeepEnabled) {//该状态表示DeepDoze的不可交互状态mState = STATE_INACTIVE;//重置标记值、取消原有的用于DeepDoze的alarm等resetIdleManagementLocked();//设置DeepAlarm,时间为30minsscheduleAlarmLocked(mInactiveTimeout, false);//30mins}//进入轻度Doze模式if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {//处理LightDoze模式进入不可交互状态mLightState = LIGHT_STATE_INACTIVE;//取消用于LightDoze的定时Alarm,重置标记值resetLightIdleManagementLocked();//设置用于LightDoze模式的Alarm,到达时间后回调//mLightAlarmListener,时间为5minsscheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);//5mins}}
}

如果要执行该方法,首要条件必须是灭屏且放电,或者mForceIdle为true,mForceIdle是用于在adb shell下使用命令设置的,因此分析时可忽略。
如果满足灭屏且放电,则分别进行DeepDoze和LightDoze的判断.这里先分析Light Doze的判断,如果当前LightDoze处于LIGHT_STATE_ACTIVE状态,且mLightEnabled为true,则进行如下的处理:

  • 1.将mLightState变为LIGHT_STATE_INACTIVE状态,表示不可交互状态(灭屏);
  • 2.重置LightDoze相关的工作和值;
  • 3.发送一个5mins的定时Alarm。

在DIC启动时,将mLightState设置为了LIGHT_STATE_ACTIVE,所以这个值是一个默认值。先来看第二点,调用了resetLightIdleManagementLocked(),该方法如下:

void resetLightIdleManagementLocked() {//取消mLightAlarmListener要接受的AlarmcancelLightAlarmLocked();
}
void cancelLightAlarmLocked() {if (mNextLightAlarmTime != 0) {mNextLightAlarmTime = 0;mAlarmManager.cancel(mLightAlarmListener);}
}

如果设有用于LightDoze的定时Alarm,则进行取消。
再来看看第三点,在scheduleLightAlarmLocked()方法中:

void scheduleLightAlarmLocked(long delay) {mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;//到达时间后,回调mLightAlarmListener.onAlarm()mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
}

结合第二点和第三点,可以认为重置了一个用于LightDoze的Alarm定时器。mLightAlarmListener用来接受Alarm的回调,定时器到达指定时间后,会触发mLightAlarmListener接口的onAlarm()方法,从而在onAlarm()中完成接下来的工作,该接口如下:

private final AlarmManager.OnAlarmListener mLightAlarmListener= new AlarmManager.OnAlarmListener() {@Overridepublic void onAlarm() {synchronized (DeviceIdleController.this) {//每次LightDoze的状态改变,都会调用该方法进行处理stepLightIdleStateLocked("s:alarm");}}
};

因此,当定时器到达时间后,通过onAlarm()方法调用stepLightIdleStateLocked()方法。stepLightIdleStateLocked()方法负责LightDoze模式的改变,在这个方法中根据不同的状态发送不同的Alarm定时器来改变状态。可以这样理解,LightDoze模式改变就是通过scheduleLightAlarmLocked()---onAlarm()---stepLightIdleStateLocked()---scheduleLightAlarmLocked()不断的循环来进行的,这样说可能不太明白,还是从代码进行分析,最后对它进行总结,该方法代码如下:

void stepLightIdleStateLocked(String reason) {//如果mLigthSate为LIGHT_STATE_OVERRIDE,说明DeepDoze处于Idle状态,由// DeepDoze将LightDoze覆盖了,因此不需要进行LightDoze了if (mLightState == LIGHT_STATE_OVERRIDE) {return;}switch (mLightState) {case LIGHT_STATE_INACTIVE://当前最小预算时间mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;//1min//表示LightDoze 进入空闲(Idle)状态的时间mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;//5mins//LightDoze进入维护状态(maintenance)的开始时间mMaintenanceStartTime = 0;//如果有活动的操作或JobScheduler、Alarm处于活动状态,//再给他们一次机会进行关闭,break返回,//否则由于没有break,会进入下条case语句if (!isOpsInactiveLocked()) {//将状态置为LIGHT_STATE_PRE_IDLE状态mLightState = LIGHT_STATE_PRE_IDLE;//设置一个定时器,到达时间后用来处理LightDoze处于PRE_IDLE//状态的操作scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);//10minsbreak;}case LIGHT_STATE_PRE_IDLE:case LIGHT_STATE_IDLE_MAINTENANCE:if (mMaintenanceStartTime != 0) {//维护状态的时长long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {// mCurIdleBudget += (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET-duration);} else {// mCurIdleBudget -= (duration-mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);}}mMaintenanceStartTime = 0;//重置维护开始时间//设置一个定时器,到达时间后用来处理LightDoze处于IDLE状态的操作scheduleLightAlarmLocked(mNextLightIdleDelay);//计算下次进入Idle状态的mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,//900000,15mins(long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;}//将LightDoze模式置为IDLE状态,开始进行一些限制mLightState = LIGHT_STATE_IDLE;addEvent(EVENT_LIGHT_IDLE);//申请一个wakelock锁,保持CPU唤醒mGoingIdleWakeLock.acquire();//处理LightDoze进入Idle状态后的操作mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);break;case LIGHT_STATE_IDLE:case LIGHT_STATE_WAITING_FOR_NETWORK:if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {//如果网络有链接或者当前LightDoze模式为等待网络状态,则进行维护,// 并将LightDoze模式退出IDLE状态,进入维护状态mActiveIdleOpCount = 1;mActiveIdleWakeLock.acquire();mMaintenanceStartTime = SystemClock.elapsedRealtime();// 保证10<=mCurIdleBudget<=30mins ,mCurIdleBudget是维护状态的时间if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;} else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;}//设置一个定时器,到达时间后用来处理LightDoze处于维护状态的操作scheduleLightAlarmLocked(mCurIdleBudget);mLightState = LIGHT_STATE_IDLE_MAINTENANCE;//进入维护状态addEvent(EVENT_LIGHT_MAINTENANCE);//处理LightDoze进入Maintenance状态后的操作mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);} else {//将LightDoze模式置为LIGHT_STATE_WAITING_FOR_NETWORK,//在进入维护状态前需要获取网络//设置一个定时器,到达时间后用来处理LightDoze处于//WAITING_FOR_NETWORK状态的操作scheduleLightAlarmLocked(mNextLightIdleDelay);//600000,5minsmLightState = LIGHT_STATE_WAITING_FOR_NETWORK;EventLogTags.writeDeviceIdleLight(mLightState, reason);}break;}
}

在这个方法中,处理了LightDoze所有的状态改变时的工作,同时也会在这个方法中进行状态的转换,各个case分析如下:

  • case LIGHT_STATE_INACTIVE:

这部分主要处理由INACTIVEIDLE/PRE_IDLE的过程。
第一次调用该方法时,LightDoze模式处于INACTIVE(不可交互状态)状态,因此会进入第一个case语句,在这个语句中,如果当前还有为处理完的任务,则会先进入IDLE状态之前的一个状态——PRE_IDLE状态,如果没有需要处理的任务,由于这里没有break,因此会继续执行到下一条case语句,将状态由INACTIVE设置为IDLE状态,并开始限制网络访问、Job延迟等处理;PRE_IDLE状态是为了再等待一次未处理完成的工作,这个状态持续5mins,直到下次Alarm时间到再次进入该方法,直接走第二条case语句。因此,在执行第二条语句之前,LightDoze处于PRE_IDLE或者INACTIVE状态。

  • case LIGHT_STATE_PRE_IDLE:
  • case LIGHT_STATE_IDLE_MAINTENANCE:

这部分主要处理由PRE_IDLE/INACTIVEIDLEMAINTENANCEIDLE的过程。在执行这两个case语句之前时,LightDoze处于PRE_IDLE或者INACTIVE状态,此时会变为IDLE状态;或者LightDoze处于MAINTENANCE,此时会变为IDLE状态。当LightDoze状态转为IDLE后,会通过Handler发送广播,通知IDLE状态的改变,开始做优化工作(无法访问网络、Job、同步延迟)。

  • case LIGHT_STATE_IDLE:
  • case LIGHT_STATE_WAITING_FOR_NETWORK:

这部分主要处理由IDLEWAITING_FOR_NETWORKWAITING_FOR_NETWORKMAINTENANCE的过程。当LightDoze状态由IDLE转换为MAINTENANCE后,会通过Handler发送广播,通知IDLE状态的改变,说明进入维护状态,开始做维护工作。
LightDoze的状态转换图如下:

下面我们就看当LightDoze状态转换为IDLEMAINTENANCE后,是如何进行功耗的优化和维护工作的。

在上面我们分析了,当由INACTIVE/PRE_IDLE状态进入IDLE状态时,和由IDLE/WAIT_FOR_NETWORK状态时,会通过Handler发送一个LightDoze模式改变的广播,通知接收器进行相应的处理,这里进行详细分析,进入IDLE状态时和进入维护状态时Handler中的相关操作如下:

@Override public void handleMessage(Message msg) {.............................switch (msg.what) {case MSG_REPORT_IDLE_ON://LightDoze模式进入IDLE状态case MSG_REPORT_IDLE_ON_LIGHT: {if (msg.what == MSG_REPORT_IDLE_ON) {//DeepDoze相关,这里略去........} else {//通知PMS设置DeepDoze模式不处于IDLE状态deepChanged = mLocalPowerManager.setDeviceIdleMode(false);//通知PMS设置轻度Doze模式处于IDLE状态lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);}try {//通知NetworkPolicyManager LightDoze进入IDLE状态mNetworkPolicyManager.setDeviceIdleMode(true);//通知BatteryStatsService统计LightDoze进入IDLE状态mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON? BatteryStats.DEVICE_IDLE_MODE_DEEP: BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());} catch (RemoteException e) {}//发送DeepDoze模式改变的广播if (deepChanged) {getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);}if (lightChanged) {//发送LightDoze模式改变的广播getContext().sendBroadcastAsUser(mLightIdleIntent,UserHandle.ALL);}//释放wakelock锁mGoingIdleWakeLock.release();} break;//退出IDLE状态时,包括DeepDozecase MSG_REPORT_IDLE_OFF: {final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);try {mNetworkPolicyManager.setDeviceIdleMode(false);mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,null, Process.myUid());} catch (RemoteException e) {}if (deepChanged) {incActiveIdleOps();getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL, null, mIdleStartedDoneReceiver,null, 0, null, null);}if (lightChanged) {incActiveIdleOps();getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,null, mIdleStartedDoneReceiver,null, 0, null, null);}decActiveIdleOps();} break;}
}

从这部分代码可以知道,在LightDoze进入IDLE状态或退出IDLE状态时时,首先通知PMS、NetworkPolicyManager设置DeviceIdleMode为true或false,然后发送一个Intent为mLightIdleIntent的广播,该mLightIdleIntent是在启动时onBootPhase()中定义:

mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY| Intent.FLAG_RECEIVER_FOREGROUND);

因此,当LightDoze进入IDLE状态时或退出IDLE状态进入MAINTENANCE状态时,都会发送该广播,其他模块如果注册了该广播接受器,则会触发onReceive()方法进行处理。当前版本中注册了mLightIdleIntent的广播接收器的模块只有一处:DeviceIdleJobsController,该类是一个用于根据Doze模式状态控制”Job”的控制类,当Doze(LightDoze和DeepDoze)模式状态为IDLE时,该类中会限制所有除白名单外的应用的”Job”,当退出IDLE状态时,该类中会解除这些应用的”Job”限制,这里所指的”Job”,时Android L后实现的一个JobScheduler机制,允许开应用在符合某些条件时创建执行在后台的任务。接受广播方法如下:

private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {final String action = intent.getAction();//当DeepDoze或LightDoze的IDLE状态改变时,都会执行updateIdleMode()if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)|| PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {updateIdleMode(mPowerManager != null? (mPowerManager.isDeviceIdleMode()|| mPowerManager.isLightDeviceIdleMode()): false);} else if (PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED.equals(action)) {updateWhitelist();}}
};

再来看下DeviceIdleJobsController中updateIdleMode()方法:

void updateIdleMode(boolean enabled) {boolean changed = false;// Need the whitelist to be ready when going into idleif (mDeviceIdleWhitelistAppIds == null) {updateWhitelist();//更新白名单列表}synchronized (mLock) {if (mDeviceIdleMode != enabled) {changed = true;}mDeviceIdleMode = enabled;//遍历所有的JobmJobSchedulerService.getJobStore().forEachJob(mUpdateFunctor);}if (changed) {//回调到JobSchedulerService中,停止或开始JobmStateChangedListener.onDeviceIdleStateChanged(enabled);}
}

在这个方法中,通过JobSchedulerService回调onDeviceIdleStateChanged()方法对后台Jobs进行限制或运行,至于如何限制,涉及到JobScheduler相关内容,不再往下分析。
再此对上面分析内容进行一次总结:
当LightDoze进入IDLE/MAINTENANCE状态时,在Handler中:

  • 1.通知NetworkPolicyManagerService限制/打开网络;
  • 2.发送广播,DeviceIdleJobsController中进行接受,限制/运行JobService.

如此反复多次,如果设备依旧保持静止且灭屏,则30分钟后,DeepDoze的Alarm时间到,因此会进入DeepDoze模式。LightDoze模式转换图示如下:

核心

1.如何限制网络?

DeviceIdleController中仅仅调用NetworkPolicyManagerService提供的接口,具体如何限制在NetworkPolicyManagerService内部实现:

mNetworkPolicyManager.setDeviceIdleMode(true);

2.如何延迟Job?

进入退出LightDoze后发送广播,DeviceIdleJobController中进行了接收,接收后也是内部进行实现:

    // onReceiveprivate final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {final String action = intent.getAction();if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)|| PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {updateIdleMode(mPowerManager != null? (mPowerManager.isDeviceIdleMode()|| mPowerManager.isLightDeviceIdleMode()): false);} else if (PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED.equals(action)) {updateWhitelist();}}};

Android 8.1 Doze模式分析(二)——Light Doze模式相关推荐

  1. Android 8.1 Doze模式分析(三)——Deep Doze模式

    概述 Deep Doze,也就是Android的Doze模式了,表示深度Doze,比起LightDoze,它将进行更多的限制:无法进行网络访问和 GPS/WLAN 扫描.唤醒被忽略.闹钟和作业/同步被 ...

  2. Android 8.1 Doze模式分析(一)——Doze简介和DeviceIdleController的启动

    概述 Doze模式,官方翻译为低电耗模式,是Andoriod6.0增加的一项系统服务,主要目的是为了优化电池性能,增加电池续航时间,Doze模式又分两种模式:深度Doze模式(Deep Doze)和轻 ...

  3. Android Doze模式分析

    Android 6.0 Doze模式分析 Doze模式是Android6.0上新出的一种模式,是一种全新的.低能耗的状态,在后台只有部分任务允许运行,其他都被强制停止.当用户一段时间没有使用手机的时候 ...

  4. Android 8.1 Doze模式分析(一)

    概述 Doze模式可以简单概括为: 若判断用户在连续的一段时间内没有使用手机,就延缓终端中APP后台的CPU和网络活动,以达到减少电量消耗的目的. Doze模式(低电耗模式),是Andoriod6.0 ...

  5. Android7.0 Doze模式分析(一)Doze介绍 amp; DeviceIdleController

     參考:http://blog.csdn.net/gaugamela/article/details/52981984 在Android M中,Google就引入了Doze模式.它定义了一种全新的 ...

  6. Android7.0 Doze模式分析(一)Doze介绍 DeviceIdleController

     参考:http://blog.csdn.net/gaugamela/article/details/52981984 在Android M中,Google就引入了Doze模式.它定义了一种全新的 ...

  7. 【朝花夕拾】Android性能篇之(二)Java内存分配

    前言       原文:[朝花夕拾]Android性能篇之(二)Java内存分配        在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给 ...

  8. Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]

    摘要:PackageManagerService在systemReady()后,进行了/system/etc/permissions中的各种xml进行扫描,进行相应的权限存储,供以后使用 阅读本文大约 ...

  9. Android系列之Fragment(二)----Fragment的生命周期和返回栈

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

最新文章

  1. c++ boost多线程学习(一)
  2. 算法之美 : 位运算
  3. BugkuCTF-WEB题alert
  4. 【转】QTableView 小结
  5. DevTools failed to load source map: Could not load content for…System error: net::ERR_FILE_NOT_FOUN
  6. Activity 的启动模式
  7. 7大国内外自动驾驶仿真平台汇总
  8. mysql主从配置详解_MySQL主从配置详解
  9. 一不小心薅了「支付宝」的羊毛
  10. 14 win7 sp1下安装vs2015
  11. 精简的webservice例子
  12. java parser_愿你走出半生,归来仍是Java Parser
  13. 快手接口分析(二)——关注
  14. html单元格浮雕效果,怎样用PS做出这种凹面浮雕效果?
  15. NYOJ 第371题 机器人II
  16. websocket-PacketCapture乱码包解密
  17. MAC 移动硬盘文件显示灰色
  18. Linux 网络文件共享服务详细介绍
  19. live555源代码分析
  20. PTA浙大版《c语言程序设计》答案集

热门文章

  1. Java基础篇——字符串处理(String,StringBuffer,StringBuild)
  2. 【经验分享】十招让你设计不恶心的PPT
  3. Android实战简易教程-第五十七枪(分享小米手电筒源码)
  4. Markdown基本语法-超级全面
  5. js取整数,取余,向上向下取整
  6. 惠泉啤酒或与燕京啤酒正面交锋
  7. 水电图纸——看图放线——3.5
  8. 常用十大算法_回溯算法
  9. 调用mpu_dmp_get_data(pitch,roll,yaw)后程序无法执行。
  10. 数据结构与算法--二进制详解 Python二进制算法详解 史上最详细的二进制讲解 彻底搞懂原码、反码、补码 Python的负数二进制表示形式