Android O Doze模式的状态

article/2025/9/15 11:28:30

现象

以下是BugReport日志
这里写图片描述

日志

STATE_ACTIVE
2,006: 11-17 10:24:59.876137   900   900 I DeviceIdleController: updateChargingLocked: charging=true 
2,007: 11-17 10:24:59.876244   900   900 I DeviceIdleController: becomeActiveLocked, reason = charging 
2,258: 11-17 10:25:35.586856   900  1037 D DeviceIdleController: handleMessage(7) 
2,285: 11-17 10:25:35.629342   900  1037 D DeviceIdleController: handleMessage(7) 
2,663: 11-17 10:26:04.366455   900   900 I DeviceIdleController: updateChargingLocked: charging=false 
3,823: 11-17 10:26:08.494908   900   900 D DeviceIdleController: updateDisplayLocked: screenOn=false STATE_ACTIVE to STATE_INACTIVE 
条件:
暗屏,不充电即可
3,824: 11-17 10:26:08.495002   900   900 D DeviceIdleController: becomeInactiveIfAppropriateLocked() 
3,825: 11-17 10:26:08.495032   900   900 D DeviceIdleController: Moved from STATE_ACTIVE to STATE_INACTIVE 
3,826: 11-17 10:26:08.495209   900   900 D DeviceIdleController: stopMonitoringMotionLocked() 
// 3 分钟 INACTIVE_TIMEOUT
3,827: 11-17 10:26:08.495311   900   900 D DeviceIdleController: scheduleAlarmLocked(180000, false) 
3,829: 11-17 10:26:08.495907   900   900 D DeviceIdleController: Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE 
// 15 秒
3,830: 11-17 10:26:08.495967   900   900 D DeviceIdleController: scheduleLightAlarmLocked(15000) 
3,831: 11-17 10:26:08.498412   900   900 V AlarmManager: APP set with listener(DeviceIdleController.light) : type=2 triggerAtTime=3649013 win=-1 tElapsed=3649013 maxElapsed=3660261 interval=0 flags=0x8 // 11-17 10:26:25 减去 11-17 10:26:08 过了17秒
3,987: 11-17 10:26:25.015547   900  1064 D AlarmManager: wakeup alarm = Alarm{2ef114b type 2 when 3649013 android}; listener package = DeviceIdleController.lightneedGrouping = false 
/** Device is inactive (screen off) and we are waiting to for the first light idle. */
// private static final int LIGHT_STATE_INACTIVE = 1;
3,988: 11-17 10:26:25.015989   900  1037 D DeviceIdleController: stepLightIdleStateLocked: mLightState=1 
// 15 秒
3,990: 11-17 10:26:25.016176   900  1037 D DeviceIdleController: scheduleLightAlarmLocked(15000) 
3,992: 11-17 10:26:25.017253   900  1037 V AlarmManager: APP set with listener(DeviceIdleController.light) : type=2 triggerAtTime=3665533 win=-1 tElapsed=3665533 maxElapsed=3676782 interval=0 flags=0x8 /** Device is in the light idle state, trying to stay asleep as much as possible. */
// private static final int LIGHT_STATE_IDLE = 4;
3,994: 11-17 10:26:25.017778   900  1037 D DeviceIdleController: Moved to LIGHT_STATE_IDLE. 
// private static final int MSG_REPORT_IDLE_ON_LIGHT = 3;
3,996: 11-17 10:26:25.018157   900  1037 D DeviceIdleController: handleMessage(3) // 11-17 10:26:41 减去 11-17 10:26:25 过了6秒
4,786: 11-17 10:26:41.010752   900  1064 D AlarmManager: wakeup alarm = Alarm{fc74c41 type 2 when 3665533 android}; listener package = DeviceIdleController.lightneedGrouping = false 
4,790: 11-17 10:26:41.017345   900  1037 D DeviceIdleController: stepLightIdleStateLocked: mLightState=4 
4,791: 11-17 10:26:41.017503   900  1037 D DeviceIdleController: scheduleLightAlarmLocked(30000) 
4,792: 11-17 10:26:41.017854   900  1037 V AlarmManager: APP set with listener(DeviceIdleController.light) : type=2 triggerAtTime=3696534 win=-1 tElapsed=3696534 maxElapsed=3719033 interval=0 flags=0x8 
// We'd like to do maintenance, but currently don't have network
// connectivity...  let's try to wait until the network comes back.
// We'll only wait for another full idle period, however, and then give up./** Device is in the light idle state, we want to go in to idle maintenance but are
* waiting for network connectivity before doing so. */
// private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
4,793: 11-17 10:26:41.018638   900  1037 D DeviceIdleController: Moved to LIGHT_WAITING_FOR_NETWORK. 4,868: 11-17 10:27:12.026656   900  1064 D AlarmManager: wakeup alarm = Alarm{aedfc2c type 2 when 3696534 android}; listener package = DeviceIdleController.lightneedGrouping = false 
//LIGHT_STATE_WAITING_FOR_NETWORK = 5;
4,870: 11-17 10:27:12.027149   900  1037 D DeviceIdleController: stepLightIdleStateLocked: mLightState=5 
// 15 秒
4,872: 11-17 10:27:12.028098   900  1037 D DeviceIdleController: scheduleLightAlarmLocked(15000) 
4,873: 11-17 10:27:12.028447   900  1037 V AlarmManager: APP set with listener(DeviceIdleController.light) : type=2 triggerAtTime=3712545 win=-1 tElapsed=3712545 maxElapsed=3723795 interval=0 flags=0x8 // 等待应用窗口事件被触发
4,876: 11-17 10:27:12.028905   900  1037 D DeviceIdleController: Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE. 
// private static final int MSG_REPORT_IDLE_OFF = 4;
4,879: 11-17 10:27:12.032376   900  1037 D DeviceIdleController: handleMessage(4) 
// private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
5,620: 11-17 10:27:12.509524   900  1037 D DeviceIdleController: handleMessage(7) 
5,651: 11-17 10:27:12.516501   900  1037 D DeviceIdleController: handleMessage(7) 
// private static final int MSG_FINISH_IDLE_OP = 8;
5,652: 11-17 10:27:13.517743   900  1037 D DeviceIdleController: handleMessage(8) 
5,653: 11-17 10:27:13.517875   900  1037 D DeviceIdleController: Exit: start=+1h1m37s545ms now=+1h1m39s35ms 
/** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
// private static final int STATE_IDLE_MAINTENANCE = 6;
5,654: 11-17 10:27:13.517936   900  1037 D DeviceIdleController: stepLightIdleStateLocked: mLightState=6 
5,655: 11-17 10:27:13.518107   900  1037 D DeviceIdleController: scheduleLightAlarmLocked(30000) 
5,656: 11-17 10:27:13.518371   900  1037 V AlarmManager: APP set with listener(DeviceIdleController.light) : type=2 triggerAtTime=3729035 win=-1 tElapsed=3729035 maxElapsed=3751535 interval=0 flags=0x8 // IDLE_MAINTENANCE 和 STATE_IDLE 是不断交替进行的,IDLE下应用特性会被限制
5,661: 11-17 10:27:13.523054   900  1037 D DeviceIdleController: Moved to LIGHT_STATE_IDLE. 
5,663: 11-17 10:27:13.523870   900  1037 D DeviceIdleController: handleMessage(3) 

时间设定

package com.android.server;/*** Keeps track of device idleness and drives low power mode based on that.*/
public class DeviceIdleController extends SystemServiceimplements AnyMotionDetector.DeviceIdleCallback {private static final boolean DEBUG = true;private static final boolean COMPRESS_TIME = true;private void updateConstants() {synchronized (DeviceIdleController.this) {try {mParser.setString(Settings.Global.getString(mResolver,mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH: Settings.Global.DEVICE_IDLE_CONSTANTS));} catch (IllegalArgumentException e) {// Failed to parse the settings string, log this and move on// with defaults.Slog.e(TAG, "Bad device idle settings", e);}// 5 分钟 或者 15 秒LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);// 10 分钟 或者 30 秒LIGHT_PRE_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_PRE_IDLE_TIMEOUT,!COMPRESS_TIME ? 10 * 60 * 1000L : 30 * 1000L);// 5 分钟 或者 15 秒LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT,!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);LIGHT_IDLE_FACTOR = mParser.getFloat(KEY_LIGHT_IDLE_FACTOR,2f);// 15 分钟 或者 60 秒LIGHT_MAX_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_MAX_IDLE_TIMEOUT,!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L);// 1 分钟 或者 15 秒LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getLong(KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,!COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L);// 5 分钟 或者 30 秒LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getLong(KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);// 5 分钟 或者 1 秒MIN_LIGHT_MAINTENANCE_TIME = mParser.getLong(KEY_MIN_LIGHT_MAINTENANCE_TIME,!COMPRESS_TIME ? 5 * 1000L : 1 * 1000L);// 30 秒 或者 5 秒MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(KEY_MIN_DEEP_MAINTENANCE_TIME,!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;// 30 分钟 或者 3 分钟INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,!COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));// 4 分钟 或者 15 秒SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,!DEBUG ? 4 * 60 * 1000L : 60 * 1000L);// 30 秒 或者 15 秒LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,!DEBUG ? 30 * 1000L : 15 * 1000L);LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);// 10 分钟 或者 60 秒MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;// 30 分钟 或者 3 分钟IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,!COMPRESS_TIME ? idleAfterInactiveTimeout: (idleAfterInactiveTimeout / 10));// 5 分钟 或者 30 秒IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_IDLE_PENDING_TIMEOUT,!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);// 10 分钟 或者 60 秒MAX_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_PENDING_TIMEOUT,!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);IDLE_PENDING_FACTOR = mParser.getFloat(KEY_IDLE_PENDING_FACTOR,2f);// 60 分钟 或者 6 分钟IDLE_TIMEOUT = mParser.getLong(KEY_IDLE_TIMEOUT,!COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);// 60 小时 或者 30 分钟MAX_IDLE_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_TIMEOUT,!COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L);IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR,2f);// 60 分钟 或者 6 分钟MIN_TIME_TO_ALARM = mParser.getLong(KEY_MIN_TIME_TO_ALARM,!COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);MAX_TEMP_APP_WHITELIST_DURATION = mParser.getLong(KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L);MMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong(KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);SMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong(KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);NOTIFICATION_WHITELIST_DURATION = mParser.getLong(KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);}}

状态变化

我截取的日志,只有以下状态
ACTIVE
INACTIVE
IDLE:doze 省电模式,应用的特性将受到抑制
IDLE_MAINTENANCE:应用窗口期,间隔退出doze模式让应用数据刷新

STATE_ACTIVE

以下任一条件
1.Motion传感器检测到运动或者未知状态
2.Voice-search事件触发
3.亮屏
4.充电
5.命令行 disable

Motion传感器检测到运动或者未知状态

    /** Device is currently active. */private static final int STATE_ACTIVE = 0;void handleMotionDetectedLocked(long timeout, String type) {....mState = STATE_ACTIVE;...}

亮屏或亮屏

void becomeActiveLocked(String activeReason, int activeUid) {....mState = STATE_ACTIVE;...}查看调用关系void exitForceIdleLocked() {if (mForceIdle) {mForceIdle = false;if (mScreenOn || mCharging) {becomeActiveLocked("exit-force", Process.myUid());}}}void updateDisplayLocked() {mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);// We consider any situation where the display is showing something to be it on,// because if there is anything shown we are going to be updating it at some// frequency so can't be allowed to go into deep sleeps.boolean screenOn = mCurDisplay.getState() == Display.STATE_ON;if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);if (!screenOn && mScreenOn) {mScreenOn = false;if (!mForceIdle) {becomeInactiveIfAppropriateLocked();}} else if (screenOn) {mScreenOn = true;if (!mForceIdle) {becomeActiveLocked("screen", Process.myUid());}}}void updateChargingLocked(boolean charging) {if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);if (!charging && mCharging) {mCharging = false;if (!mForceIdle) {becomeInactiveIfAppropriateLocked();}} else if (charging) {mCharging = charging;if (!mForceIdle) {becomeActiveLocked("charging", Process.myUid());}}}

命令行 disable

    void becomeActiveLocked(String activeReason, int activeUid) {....mState = STATE_ACTIVE;...}查看调用关系if (becomeActive) {becomeActiveLocked((arg == null ? "all" : arg) + "-disabled",Process.myUid());}
    public void exitIdleInternal(String reason) {synchronized (this) {becomeActiveLocked(reason, Binder.getCallingUid());}}@Override public void exitIdle(String reason) {getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,null);long ident = Binder.clearCallingIdentity();try {exitIdleInternal(reason);} finally {Binder.restoreCallingIdentity(ident);}}SourceInsight的搜索结果,为voice-search
DeviceIdleController.java (base\services\core\java\com\android\server):        @Override public void exitIdle(String reason) {
IDeviceIdleController.aidl (base\core\java\android\os):    void exitIdle(String reason);
PhoneWindowManager.java (base\services\core\java\com\android\server\policy):                            dic.exitIdle("voice-search");
PhoneWindowManager.java (base\services\core\java\com\android\server\policy): 

STATE_ACTIVE to STATE_INACTIVE

同时满足条件:
暗屏,不充电即可

    /** Device is inactive (screen off, no motion) and we are waiting to for idle. */private static final int STATE_INACTIVE = 1;void becomeInactiveIfAppropriateLocked() {if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");if ((!mScreenOn && !mCharging) || mForceIdle) {// Screen has turned off; we are now going to become inactive and start// waiting to see if we will ultimately go idle.if (mState == STATE_ACTIVE && mDeepEnabled) {mState = STATE_INACTIVE;if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");resetIdleManagementLocked();scheduleAlarmLocked(mInactiveTimeout, false);EventLogTags.writeDeviceIdle(mState, "no activity");}if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {mLightState = LIGHT_STATE_INACTIVE;if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");resetLightIdleManagementLocked();scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);EventLogTags.writeDeviceIdleLight(mLightState, "no activity");}}}// 查看时间mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;// mHasWatch == false,即 30 分钟 或者 3分钟long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,!COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));

Moved to LIGHT_STATE_IDLE

该模式下应用特性会被限制

日志

3,994: 11-17 10:26:25.017778 900 1037 D DeviceIdleController: Moved to LIGHT_STATE_IDLE.

===============================================================
/** Device is in the light idle state, trying to stay asleep as much as possible. */
// private static final int LIGHT_STATE_IDLE = 4;===============================================================
case LIGHT_STATE_IDLE: return "IDLE";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) {// We didn't use up all of our minimum budget; add this to the reserve.mCurIdleBudget += (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET-duration);} else {// We used more than our minimum budget; this comes out of the reserve.mCurIdleBudget -= (duration-mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);}}mMaintenanceStartTime = 0;scheduleLightAlarmLocked(mNextLightIdleDelay);mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,(long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;}if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");mLightState = LIGHT_STATE_IDLE;EventLogTags.writeDeviceIdleLight(mLightState, reason);addEvent(EVENT_LIGHT_IDLE);mGoingIdleWakeLock.acquire();mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);break;===============================================================/** Device is about to go idle for the first time, wait for current work to complete. */private static final int LIGHT_STATE_PRE_IDLE = 3;/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;===============================================================    LIGHT_PRE_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_PRE_IDLE_TIMEOUT,!COMPRESS_TIME ? 10 * 60 * 1000L : 30 * 1000L);

Moved to LIGHT_WAITING_FOR_NETWORK 本质还是IDLE

4,793: 11-17 10:26:41.018638 900 1037 D DeviceIdleController: Moved to LIGHT_WAITING_FOR_NETWORK.

===============================================================case LIGHT_STATE_IDLE:case LIGHT_STATE_WAITING_FOR_NETWORK:if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {// We have been idling long enough, now it is time to do some work...........} else {// We'd like to do maintenance, but currently don't have network// connectivity...  let's try to wait until the network comes back.// We'll only wait for another full idle period, however, and then give up.scheduleLightAlarmLocked(mNextLightIdleDelay);if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;EventLogTags.writeDeviceIdleLight(mLightState, reason);}break;
===============================================================/** Device is in the light idle state, trying to stay asleep as much as possible. */private static final int LIGHT_STATE_IDLE = 4;/** Device is in the light idle state, we want to go in to idle maintenance but are* waiting for network connectivity before doing so. */private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;

LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE

            case LIGHT_STATE_IDLE:case LIGHT_STATE_WAITING_FOR_NETWORK:if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {// We have been idling long enough, now it is time to do some work.mActiveIdleOpCount = 1;mActiveIdleWakeLock.acquire();mMaintenanceStartTime = SystemClock.elapsedRealtime();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;}scheduleLightAlarmLocked(mCurIdleBudget);if (DEBUG) Slog.d(TAG,"Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");mLightState = LIGHT_STATE_IDLE_MAINTENANCE;EventLogTags.writeDeviceIdleLight(mLightState, reason);addEvent(EVENT_LIGHT_MAINTENANCE);mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);

Doze模式的完整状态转换

这里写图片描述

具体解释看源码

   void stepIdleStateLocked(String reason) {if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);EventLogTags.writeDeviceIdleStep();final long now = SystemClock.elapsedRealtime();if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {// Whoops, there is an upcoming alarm.  We don't actually want to go idle.if (mState != STATE_ACTIVE) {becomeActiveLocked("alarm", Process.myUid());becomeInactiveIfAppropriateLocked();}return;}switch (mState) {case STATE_INACTIVE:// We have now been inactive long enough, it is time to start looking// for motion and sleep some more while doing so.startMonitoringMotionLocked();// 运动检测// 5 分钟scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);// Reset the upcoming idle delays.// 30 分钟mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;mNextIdleDelay = mConstants.IDLE_TIMEOUT;// 切换当前状态为STATE_IDLE_PENDINGmState = STATE_IDLE_PENDING;if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");EventLogTags.writeDeviceIdle(mState, reason);break;case STATE_IDLE_PENDING:// 切换当前状态为STATE_SENSING// IDLE_PENDING_TIMEOUT 30 分钟后才可以到这个条件哦mState = STATE_SENSING;if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");EventLogTags.writeDeviceIdle(mState, reason);// 4 分钟scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);cancelLocatingLocked();mNotMoving = false;mLocated = false;mLastGenericLocation = null;mLastGpsLocation = null;mAnyMotionDetector.checkForAnyMotion();break;case STATE_SENSING:cancelSensingTimeoutAlarmLocked();// 切换当前状态为STATE_LOCATING// 不移动且达到 IDLE_AFTER_INACTIVE_TIMEOUT 5 分钟才可以到这个条件哦mState = STATE_LOCATING;if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");EventLogTags.writeDeviceIdle(mState, reason);// 30 秒scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);if (mLocationManager != null&& mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {mLocationManager.requestLocationUpdates(mLocationRequest,mGenericLocationListener, mHandler.getLooper());mLocating = true;} else {mHasNetworkLocation = false;}if (mLocationManager != null&& mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {mHasGps = true;mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,mGpsLocationListener, mHandler.getLooper());mLocating = true;} else {mHasGps = false;}// If we have a location provider, we're all set, the listeners will move state// forward.if (mLocating) {break;}// Otherwise, we have to move from locating into idle maintenance.case STATE_LOCATING:// 切换当前状态为STATE_LOCATING// 角度无改变持续且4分钟才可以到这个状态cancelAlarmLocked();cancelLocatingLocked();mAnyMotionDetector.stop();case STATE_IDLE_MAINTENANCE:// 切换当前状态为STATE_IDLE_MAINTENANCE// GPS无变化且30秒才可以到这个状态scheduleAlarmLocked(mNextIdleDelay, true);if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +" ms.");mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {mNextIdleDelay = mConstants.IDLE_TIMEOUT;}mState = STATE_IDLE;if (mLightState != LIGHT_STATE_OVERRIDE) {mLightState = LIGHT_STATE_OVERRIDE;cancelLightAlarmLocked();}EventLogTags.writeDeviceIdle(mState, reason);addEvent(EVENT_DEEP_IDLE);mGoingIdleWakeLock.acquire();mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);break;case STATE_IDLE:// 系统会在这个两个状态下(STATE_IDLE与STATE_IDLE_MAINTENANCE)进行交替切换// We have been idling long enough, now it is time to do some work.mActiveIdleOpCount = 1;mActiveIdleWakeLock.acquire();scheduleAlarmLocked(mNextIdlePendingDelay, false);if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +"Next alarm in " + mNextIdlePendingDelay + " ms.");mMaintenanceStartTime = SystemClock.elapsedRealtime();mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;}mState = STATE_IDLE_MAINTENANCE;EventLogTags.writeDeviceIdle(mState, reason);addEvent(EVENT_DEEP_MAINTENANCE);mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);break;}}

结语

Doze模式的省电效果毋庸置疑,我们关注这个模式切换的好处,就是能否借鉴这个思路,进行精确的空闲模式识别。新增一些静默重启的操作,来解决android 系统过于内存碎片化,或者准确识别夜间模式,而不是通过时间进行考量。


http://chatgpt.dhexx.cn/article/UI6R4JU3.shtml

相关文章

【Android】Doze模式识别与检测

从 Android 6.0&#xff08;API 级别 23&#xff09;开始&#xff0c;Android 引入了两个省电功能&#xff1a;Doze模式&#xff08;官方翻译为低电耗模式&#xff09;和 App Standby模式&#xff08;官方翻译为应用待机模式&#xff09;&#xff0c;可通过管理应用在设备未连接…

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

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

Android 6.0的省电技术Doze作用影响以及避免方法

从android 6.0开始&#xff0c;谷歌引入了两项新的省电技术延长电池使用时间&#xff0c;分别是Doze&#xff08;休眠&#xff09;和App Standby&#xff08;app待命模式&#xff09;&#xff0c;只要app是运行在6.0&#xff08;api 23&#xff09;及以上的系统&#xff0c;无论…

Doze模式简介

Doze模式是自Android 6.0开始引入的两项省电功能的其中之一&#xff0c;还有一个就是appstandby&#xff0c;通过管理应用在设备未连接至电源时的行为方式&#xff0c;帮助用户延长电池寿命。当用户长时间未使用设备时&#xff0c;低电耗模式会延迟应用的后台 CPU 和网络活动&a…

Android中的Doze模式

概述 Android 6.0引入的Doze机制在于节省系统耗电量&#xff0c;保护电池&#xff0c;延长电池的使用时间。当设备未连接至电源&#xff0c;且长时间处于闲置状态时&#xff0c;系统会将应用进入Doze&#xff0c;置于App Standby模式。而最终的效果&#xff0c;能提升30%的电量…

Mock工具之Mockito实战

在实际项目中写单元测试的过程中我们会发现需要测试的类有很多依赖&#xff0c;这些依赖项又会有依赖&#xff0c;导致在单元测试代码里几乎无法完成构建&#xff0c;尤其是当依赖项尚未构建完成时会导致单元测试无法进行。为了解决这类问题我们引入了Mock的概念&#xff0c;简…

Mockito 入门

目录 1.什么是 Mock 测试&#xff1f;2.Mockito简介3.在 SpringBoot 单元测试中使用 Mockito3.1 Maven依赖&#xff1a;3.2 UserService.java3.3 User.java3.4 thenReturn系列方法&#xff08;测试桩&#xff09;3.5 thenThrow系列方法3.6 verify 系列方法 4.Spring中mock任何容…

Mock工具之Moco使用教程

目录 一、什么是Moco二、安装&配置三、接口配置&测试3.1 第一个简单的请求&#xff1a;3.2 Get请求&#xff1a;3.3 Post请求&#xff1a;3.4 返回值为Json格式的请求&#xff1a;3.5 带cookie信息才能访问的post请求&#xff1a;3.6 重定向请求&#xff1a;3.7 前后端…

mock功能

目标 了解mock的作用及使用场景&#xff1b; mock使用场景 mock&#xff1a;假的 前端程序员提到的mock数据的含义是&#xff1a;真的假数据 真的&#xff1a;符合接口规范要求的。 假数据&#xff1a;数据是人为创建出来的&#xff0c;不是真正的业务数据。 什么时候需…

mock的介绍

1.什么是mock mock测试是以可控的方式模拟真实的对象行为。程序员通常创造模拟对象来测试对象本身该具备的行为&#xff0c;很类似汽车设计者使用碰撞测试假人来模拟车辆碰撞中人的动态行为 2.为什么要使用mock 之所以使用mock测试&#xff0c;是因为真实场景很难实现或者短…

Mock介绍

mock的定义&#xff08;what&#xff09;&#xff1a; mock是在测试过程中&#xff0c;对于一些不容易构造/获取的对象&#xff0c;创建一个mock对象来模拟对象的行为 为什么要使用mock&#xff08;why&#xff09;&#xff1a; 在做单元测试过程中&#xff0c;经常会有以下的…

Mockito单元测试

文章目录 Mockito单元测试为什么要使用Mock?导入依赖import导入包使用Mock模拟测试某个类中的某个方法是否可以成功执行使用Mock模拟某个类的方法&#xff0c;自己给这个方法返回我们指定的值使用Mock模拟某个方法调用后会抛出指定的异常使用Mock模拟测试某个类中的某个方法(加…

javascript 中怎么判断为数字类型

javascript中判断变量是否为数字类型抄的方法有两种&#xff1a; 方法一&#xff1a; function isnum(s) { if(s!null){ var r,re; re /\d*/i; //\d表示数字,*表示匹配多个数字 r s.match(re); return (rs)?true:false; } return false; } 方法二&#xff1a; function isnu…

js判断字符串是不是一个纯数字

1.使用隐式转换判断 使用parseInt()/parseFloat()会把其他类型转换为数值&#xff0c;转换结果为数值或NaN,转换字符串的时候&#xff0c;头部内容有数值就会截取前面&#xff0c;如123456aff789bb转成123456&#xff0c;后面即使有数字也不会转换处理&#xff0c;开头如果是字…

javaScript 判断是否为数值类型的三种方法

第一种方法:isNaN(value) isNaN : isNaN() 函数用于检查其参数是否是非数字值。 返回true则判断为非数值。缺点&#xff1a;空字符串&#xff0c;空格 &#xff0c;null会做为数字0进行处理&#xff0c;结果为falsehttps://www.w3school.com.cn/js/jsref_isnan.anusp 第二种方法…

JS中判断变量是否为数字方法

推荐教程&#xff1a;《JavaScript视频教程》 JavaScript 是一种动态类型语言&#xff0c;这意味着解释器在运行时确定变量的类型。实际上&#xff0c;这也允许我们在相同的代码中使用相同的变量来存储不同类型的数据。如果没有文档和一致性&#xff0c;我们在使用代码时并不总…

matlab绘制图形中,常用函数调用(num2str,disp,gcf,hold on,plot,axis,subplot,line,stairs,grid,set,gca)

num2str 将数字转换为字符数组 s = num2str(A) 将数字数组转换为表示数字的字符数组。输出格式取决于原始值的大小。 num2str对于使用数值标注和标题绘图非常有用。 s = num2str(A,precision) 返回一个字符数组,表示具有精度指定的最大有效位数的数字。 s = num2str(A,fo…

Matlab中num2str函数使用

目录 语法 说明 示例 浮点值的默认转换 指定精度 指定格式 num2str函数将数字转换为字符数组。 语法 s num2str(A)s num2str(A,precision)s num2str(A,formatSpec) 说明 s num2str(A) 将数值数组转换为表示数字的字符数组。输出格式取决于原始值的量级。num2str 对…

C/C++ split函数 num2str str2num函数

C/C split函数,num2str及str2num函数实现 在C98及其以下版本的C/C中&#xff0c;没有现成的字符串split函数和num2str及str2num函数&#xff0c;对于竞赛不能使用C11的我来说非常头疼&#xff0c;通过学习stringstream和strtok实现了自己的split函数&#xff0c;通过stringstr…

matlab 中num2str函数的使用

运行ECO代码报错&#xff1a;转义字符 ‘\d’ 无效。有关支持的特殊字符&#xff0c;请参阅 ‘doc sprintf’。 num2str函数功能&#xff1a;将数字转换为字符串 具体所有功能可见官网。 这里使用的格式为&#xff1a; str num2str(A, format) 将 format 指定的格式应用到 A …