精华内容
下载资源
问答
  • Android Doze模式分析

    万次阅读 2016-09-12 15:01:23
    Android 6.0 Doze模式分析 Doze模式Android6.0上新出的一种模式,是一种全新的、低能耗的状态,在后台只有部分任务允许运行,其他都被强制停止。当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的...

    Android 6.0 Doze模式分析

    Doze模式是Android6.0上新出的一种模式,是一种全新的、低能耗的状态,在后台只有部分任务允许运行,其他都被强制停止。当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络活动减少电量的消耗。PowerManagerService中也有Doze模式,和此处的Doze模式不一样,其实此处叫Device Idle模式更容易区分

    如果一个用户断开了充电连接,关屏不动手机一段时间之后,设备进入Doze模式。在Doze模式中,系统尝试去通过减少应用的网络访问和CPU敏感的服务来保护电池。它也阻止应用通过访问网络,并且延缓应用的任务、同步和标准alarms。

    系统定期退出Doze模式(maintenance window)去让app完成他们被延缓的动作。在maintenance window期间,系统运行所有挂起的同步、任务和alarms,同时也能访问网络

    A


    Doze模式的限制。

    1. 网络接入被暂停

    2. 系统忽略wake locks

    3. 标准的AlarmManager alarms被延缓到下一个maintenance window

    4. 如果你需要在Doze状态下启动设置的alarms,使用setAndAllowWhileIdle()或者setExactAndAllowWhileIdle()。

    5. 当有setAlarmClock()的alarms启动时,系统会短暂退出Doze模式

    6. 系统不会扫描Wi-Fi

    7. 系统不允许sync adapters运行

    8. 系统不允许JobScheduler运行

    Doze模式在系统中主要有DeviceIdleController来驱动。下面我们来分析下DeviceIdleController

    DeviceIdleController 的启动和初始化

    DeviceIdleController和PowerManagerService一样都继承自SystemService类,同样是在SystemServer服务中启动。

    mSystemServiceManager.startService(DeviceIdleController.class);

    同样,在SystemServiceManager中的startService方法中利用反射的方法构造DeviceIdleController对象,然后调用DeviceIdleController的onStart方法来初始化。

    DeviceIdleController的构造方法

    public DeviceIdleController(Context context) {
            super(context);
            mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
            mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
        }

    构造方法很简单,只有两步

    1. 创建一个deviceidle.xml文件,该文件位于data/system/目录下。

    2. 创建了一个Handler用来处理消息

    onStart方法

    public void onStart() {
            ……
            synchronized (this) {
               //第1步,获取Doze模式是否默认开启
                mEnabled = getContext().getResources().getBoolean(
                        com.android.internal.R.bool.config_enableAutoPowerModes);
               //第2步,从systemConfig中读取默认的系统应用的白名单
                SystemConfig sysConfig = SystemConfig.getInstance();
                ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();
                for (int i=0; i<allowPowerExceptIdle.size(); i++) {
                    String pkg = allowPowerExceptIdle.valueAt(i);
                    try {
                        ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
                        if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
                            int appid = UserHandle.getAppId(ai.uid);
                            mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                            mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
                        }
                    } catch (PackageManager.NameNotFoundException e) {
                    }
                }
                //第3步
                ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
                for (int i=0; i<allowPower.size(); i++) {
                    String pkg = allowPower.valueAt(i);
                    try {
                        ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
                        if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
                            int appid = UserHandle.getAppId(ai.uid);
                            mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                            mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
                            mPowerSaveWhitelistApps.put(ai.packageName, appid);
                            mPowerSaveWhitelistSystemAppIds.put(appid, true);
                        }
                    } catch (PackageManager.NameNotFoundException e) {
                    }
                }
    
                mConstants = new Constants(mHandler, getContext().getContentResolver());
                //第4步
                readConfigFileLocked();
                //第5步
                updateWhitelistAppIdsLocked();
                //第6步
                mScreenOn = true;
                mCharging = true;
                mState = STATE_ACTIVE;
                mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
            }
            //第7步
            publishBinderService(Context.DEVICE_IDLE_CONTROLLER, new BinderService());
            publishLocalService(LocalService.class, new LocalService());
        }

    这个方法中大致可以分为6部分

    第1步:从配置文件中获取Doze模式的开关值,默认为false

    第2步:从SystemConfig中读取Doze模式系统应用的白名单,这个白名单是已经在系统配置文件中配置好的,位于手机目录system/ect/sysconfig中。

    收集了配置的除了Idle模式都可以运行的白名单

    第3步:从SystemConfig中读取Doze模式的白名单

    第2步和第3步主要用于读取Doze模式下系统应用的白名单。

    第4步:读取deviceidle.xml文件,解析xml文件并将用户应用的白名单读入内存

    第5步:设置Doze模式的白名单,通过updateWhitelistAppIdsLocked()方法将系统应用白名单和用户应用的白名单合并,然后将白名单设置到PowerManagerService中

    mLocalPowerManager.setDeviceIdleTempWhitelist(mTempWhitelistAppIdArray);

    第6步:初始化一些变量,默认系统的屏幕为开启,Doze模式默认为ACTIVE,默认为充电模式等

    第7步:和PowerManagerService类似,将DeviceIdleController注册到ServiceManager和LocalService中。

    OnStart一共大致有以上7个步骤,主要作用是读取系统白名单和应用白名单,并设置到PowerManagerService中。

    下一步同样和PowerManangerService一样,当SystemSerivce Ready之后回调 onBootPhase方法,这个方法也比较简单,主要是也是做一些初始化的功能。


    OnBootPhase方法

    public void onBootPhase(int phase) {
            if (phase == PHASE_SYSTEM_SERVICES_READY) {
                synchronized (this) {
                    ……
                    //初始化部分传感器服务
                    mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
                    mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
                    mLocationManager = (LocationManager) getContext().getSystemService(
                            Context.LOCATION_SERVICE);
                    ……
                    mAnyMotionDetector = new AnyMotionDetector(
                            (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
                            mHandler, mSensorManager, this);
    
                    ……
                    //注册电池变化等广播
                    IntentFilter filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                    filter.addAction(ACTION_STEP_IDLE_STATE);
                    getContext().registerReceiver(mReceiver, filter);
                    ……
    
                   //更新白名单mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                    //注册屏幕显示的回调方法
                    mDisplayManager.registerDisplayListener(mDisplayListener, null);
                    updateDisplayLocked();
                }
            }
        }

    可以看出onBootPhase方法主要是初始化一些传感器,注册了电池改变的广播接收器,注册了屏幕显示变化的回调方法,最后调用updateDisplayLocked方法。

    由于DeviceIdleController刚启动,初始化的时候,screenOn默认为true且屏幕状态未发生变化,最后直接调用becomeActiveLocked()方法,将Doze状态设置为ACTIVE,并初始化一些值。

    至此,DeviceIdleController就基本启动和初始化完成了。逻辑比较简单,下面我们将讨论Doze模式的几种状态及切换逻辑

     DeviceIdleController在onBootPhase方法中,注册了一个ACTION_BATTERY_CHANGED广播接收器,当电池状态发生变化的时候触发该广播接收器的onReceive方法

    if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
                    int plugged = intent.getIntExtra("plugged", 0);
                    updateChargingLocked(plugged != 0);
                }

    当Battery状态发生变化的时候,首先先根据Intent的参数判断是否是充电状态,然后调updateChargingLocked 方法

    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());
                }
            }
        }

    该方法中,根据当前的充电状态,如果是由充电状态变为未充电状态的时候,调用becomeInactiveIfAppropriateLocked()方法,修改Doze模式的状态为InActive状态,如果是充电状态则直接调用becomeActiveLocked方法修改Doze模式的状态为Active状态。

    同时在onBootPhase方法中我们还设置了displayListener的监听方法,当屏幕显示状态发生变化的时候,回调该接口。

    public void onDisplayChanged(int displayId) {
                if (displayId == Display.DEFAULT_DISPLAY) {
                    synchronized (DeviceIdleController.this) {
                        updateDisplayLocked();
                    }
                }

    当Display发生变化的时候,最终调用updateDisplayLocked来更新状态

    void updateDisplayLocked() {
            mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
            boolean screenOn = mCurDisplay.getState() == Display.STATE_ON;
            if (!screenOn && mScreenOn) {
                mScreenOn = false;
                if (!mForceIdle) {
                    becomeInactiveIfAppropriateLocked();
                }
            } else if (screenOn) {
                mScreenOn = true;
                if (!mForceIdle) {
                    becomeActiveLocked("screen", Process.myUid());
                }
            }
        }

    逻辑也比较简单,首先获得当前显示状态,如果显示状态是由亮屏到灭屏,那么调用becomeInactiveIfAppropriateLocked()方法,将Doze模式的状态设置为InActive,如果当前状态是亮屏状态,这直接调用becomeActiveLocked将Doze模式的状态设置为Active。


    Doze模式状态转换分析

    becomeInactiveIfAppropriateLocked方法分析


    void becomeInactiveIfAppropriateLocked() {
            if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) {
                mState = STATE_INACTIVE;
                resetIdleManagementLocked();
                scheduleAlarmLocked(mInactiveTimeout, false);
            }
        }

    首先屏幕灭屏并且不是充电状态,Doze状态为Active的时候,

    1. 将状态修改为InActice

    2. 重置一些变量及状态

    3. 设置一个定时器,在一段时间mInactiveTimeout后触发。时间mInactiveTimeout = 30min

    void resetIdleManagementLocked() {
            //重置Idle和maintance状态中的时间间隔
            mNextIdlePendingDelay = 0;
            mNextIdleDelay = 0;
            //取消定时器
            cancelAlarmLocked();
            //取消传感器和定位监测
            cancelSensingAlarmLocked();
            cancelLocatingLocked();
            stopMonitoringSignificantMotion();
            mAnyMotionDetector.stop();
        }

    mNextIdlePendingDelaymNextIdleDelay两个时间变量重置,取消定时器,停止定位和运动,位置检测。

    becomeActiveLocked方法分析

    void becomeActiveLocked(String activeReason, int activeUid) {
            if (mState != STATE_ACTIVE) {
                scheduleReportActiveLocked(activeReason, activeUid);
                mState = STATE_ACTIVE;
                mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
                resetIdleManagementLocked();
            }
        }

    主要是将Doze的状态设置为ACTIVE状态,并重置相关的变量。执行scheduleReportActiveLocked方法,该方法发送了一个MESSAGE_REPORT_ACTIVE的消息给Handler。

    case MSG_REPORT_ACTIVE: {
                        String activeReason = (String)msg.obj;
                        int activeUid = msg.arg1;
                        boolean needBroadcast = msg.arg2 != 0;
                        mLocalPowerManager.setDeviceIdleMode(false);
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(false);
                            mBatteryStats.noteDeviceIdleMode(false, activeReason, activeUid);
                        } catch (RemoteException e) {
                        }
                        if (needBroadcast) {
                            getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                        }
                    } break

    Handler处理逻辑,主要告诉PowerManagerService退出Doze模式,网络和电量统计退出Doze模式。

    该方法主要作用是修改Doze模式为Active,并通知相应的服务退出Doze模式。

    下一步我们接着分析InActive状态。当拔掉充电电源的时候或者屏幕灭屏的时候,调用becomeInactiveIfAppropriateLocked进入InActive状态,当30min后,定时器触发后,广播接收器接收到ACTION_STEP_IDLE_STATE定时器触发,并调用了setIdleStateLocked方法。

    setIdleStateLocked方法关键代码:

    switch (mState) {
                case STATE_INACTIVE:
                    startMonitoringSignificantMotion();
                    scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);.
                    mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                    mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                    mState = STATE_IDLE_PENDING;
                    break;
    				……

    1. 调用startMonitoringSignificantNotion()方法,注册SigMotion传感器,监测重要的运动事件。

    2. 重新设置一个定时器,同样在30min后触发。

    3. 设置mNextIdlePendingDelay的时间为5min, mNextIdleDelay的时间为60min

    4. 修改当前的Doze状态为IDLE_PENDING状态

    startMonitoringSignificantNotion()方法注册了运动监测的传感器,当传感器触发的时候,回调SigMotionListener接口,最终调用significantMotionLocked()方法来处理

    void significantMotionLocked() {
            mSigMotionActive = false;
            handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
        }
    
        void handleMotionDetectedLocked(long timeout, String type) {
            if (mState != STATE_ACTIVE) {
                scheduleReportActiveLocked(type, Process.myUid());
                mState = STATE_ACTIVE;
                mInactiveTimeout = timeout;
                cancelSensingAlarmLocked();
                becomeInactiveIfAppropriateLocked();
            }
        }

    处理逻辑,当前的状态为Idle_pending,所以调用scheduleReportActiveLocked方法通知相关的服务退出Doze模式,将当前状态修改为Active,最终调用becomeInactiveIfAppropriateLocked重新进入InActive状态。根据此处逻辑可知,如果传感器监测到有特殊的运动就随时返回到InActive状态。

    如果没有运动30min后触发定时器,再次进入setIdleStateLocked方法

    case STATE_IDLE_PENDING:
                    mState = STATE_SENSING;
                    scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT);
                    cancelLocatingLocked();
                    mAnyMotionDetector.checkForAnyMotion();
                    mNotMoving = false;
                    mLocated = false;
                    mLastGenericLocation = null;
                    mLastGpsLocation = null;
                    break;

    此时将当前的Doze状态设置为STATE_SENSING,同时设置了定时器,4min后触发。同时开启运动检测,mAnyMotionDetector.checkForAnyMotion(),该方法利用传感器,检测手机是否移动,我们来看下关键代码实现。

    if (!mMeasurementInProgress && mAccelSensor != null) {
                if (mSensorManager.registerListener(mListener, mAccelSensor,
                        SAMPLING_INTERVAL_MILLIS * 1000)) {
                    mWakeLock.acquire();
                    mMeasurementInProgress = true;
                    mRunningStats.reset();
                }
    
                Message msg = Message.obtain(mHandler, mMeasurementTimeout);
                msg.setAsynchronous(true);
                mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
            }

    此处注册了个传感器,然后接受传感器的数据,然后延迟3s执行方法。

    首先,接收数据接口,数据接口会判断当前的数据信息是否足够,若足够则调用status = stopOrientationMeasurementLocked(),根据当前传感器的数据计算出当前手机的状态,回调.onAnyMotionResult(status)方法。

    或者,延迟3s再收集数据,调用status = stopOrientationMeasurementLocked(),根据当前传感器的数据计算出当前手机的状态,回调.onAnyMotionResult(status)方法。

    public void onAnyMotionResult(int result) {
            if (result == AnyMotionDetector.RESULT_MOVED) {
                synchronized (this) {
                    handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "sense_motion");
                }
            } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
                if (mState == STATE_SENSING) {
                    synchronized (this) {
                        mNotMoving = true;
                        stepIdleStateLocked();
                    }
                } 
    ……
        }

    当检测到手机的状态后,根据状态处理不同的逻辑

    1. 当手机处于MOVE状态的时候,执行handleMotionDetectedLocked方法,将状态重置为INACTIVE状态,通知各个服务退出IDLE模式。

    2. 当手机固定不动的时候,讲notMoveing变量置为true,同时执行

    setIdleStateLocked方法,进入下一个状态。

    case STATE_SENSING:
                    mState = STATE_LOCATING;
                    scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);
                    mLocating = true;
                    mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener,
                            mHandler.getLooper());
                    if (mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
                        mHaveGps = true;
                        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
                                mGpsLocationListener, mHandler.getLooper());
                    } else {
                        mHaveGps = false;
                    }
                    break;

    当移动监测完成,或者4min后定时器触发,当前状态为STATE_LOCATION状态。在设置30s的定时器,同时调用系统定位,当定位完成回调定位完成接口

    void receivedGenericLocationLocked(Location location) {
           ……
            mLocated = true;
            if (mNotMoving) {
                stepIdleStateLocked();
            }
        }
    
        void receivedGpsLocationLocked(Location location) {
            ……
            mLocated = true;
            if (mNotMoving) {
                stepIdleStateLocked();
            }
        }

    当接收到定位信息且当前手机位置没有移动,就进入IDLE状态。

          case STATE_LOCATING:
                    cancelSensingAlarmLocked();
                    cancelLocatingLocked();
                    mAnyMotionDetector.stop();
                case STATE_IDLE_MAINTENANCE:
                    scheduleAlarmLocked(mNextIdleDelay, true);
                    mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
                    mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
                    mState = STATE_IDLE;
                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
                    break;

    先取消上个状态的没有完成的定时器和定位,取消运动监测。设置下一个定时器mNextIdleDelay = 60min后触发。修改mNextIdleDelay的值为当前的2倍。最大值为6h,将状态设置为IDLE状态,发送IDLE的handler消息,接收到消息通知相关的服务进入IDLE状态。

    当60min过后,定时器触发,进入IDLE_MAINTENANCE状态。设置定时器5min后触发,修改下次时间为2倍,最大时间为10min,发送MSG_REPORT_IDLE_OFF消息,在Handler中处理,通知各个服务退出IDLE状态。

    case STATE_IDLE:	
                    scheduleAlarmLocked(mNextIdlePendingDelay, false);
                    mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
                            (long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
                    mState = STATE_IDLE_MAINTENANCE;
                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                    break;

    下次触发再次进入IDLE状态。

    Doze模式的几个状态转换基本分析完成,下面是各个状态转换的流程图

    V


    Doze模式总共有7中状态,手机被操作的时候为Active状态,当手机关闭屏幕或者拔掉电源的时候,手机开始进入Doze模式,经过一系列的状态后最终进入IDLE状态,此时属于深度休眠状态,系统中的网络,Wakelock等服务都会停止运行,当60min过后,系统进入IDLE_MAINTENANCE状态,此时集中处理相关的任务,5min后再次进入IDLE状态,每次进入IDLE状态,时间都会是上次的2倍,最大时间限制为6h.

    在这7中状态中,随时都会返回ACTIVE或者INACTIVE状态。当手机运动,或者点亮屏幕,插上电源等,系统会返回到ACTIVIE和INACTIVE状态。

    PowerManagerServiceDoze状态的处理

    以上分析完了Doze几个状态之间的转换,下面我们分析下PowerManagerService中关于Doze的处理逻辑。

    我们知道,当Doze模式转换到Idle状态之后,就会通知相关的服务进入IDLE状态,其中PowerManangerService处理的方法为localPowerManager.setDeviceIdle(true)

    void setDeviceIdleModeInternal(boolean enabled) {
            synchronized (mLock) {
                if (mDeviceIdleMode != enabled) {
                    mDeviceIdleMode = enabled;
                    updateWakeLockDisabledStatesLocked();
                }
            }
        }

    将PowerManagerService中mDeviceIdleMode的值设置为true,然后调用updateWakeLockDisableStatesLocked()方法更新wakeLock的状态

    private void updateWakeLockDisabledStatesLocked() {
            boolean changed = false;
            final int numWakeLocks = mWakeLocks.size();
            for (int i = 0; i < numWakeLocks; i++) {
                final WakeLock wakeLock = mWakeLocks.get(i);
                if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
                        == PowerManager.PARTIAL_WAKE_LOCK) {
                    if (setWakeLockDisabledStateLocked(wakeLock)) {
                        changed = true;
                        if (wakeLock.mDisabled) {
                            //当前wakeLock的mDisabled变量为true,释放掉该wakeLock
                            notifyWakeLockReleasedLocked(wakeLock);
                        } else {
                            notifyWakeLockAcquiredLocked(wakeLock);
                        }
                    }
                }
            }
            if (changed) {
                mDirty |= DIRTY_WAKE_LOCKS;
                updatePowerStateLocked();
            }
        }

    在更新WakeLock的信息时,遍历所有的wakeLock,只处理wakeLock的类型为PARTIAL_WAKE_LOCK的wakeLock。调用setWakeLockDisabledStateLocked(wakeLock)方法更新wakeLock的disable的状态值。在该方法中根据当前wakelock所有应用程序的appid来判断该程序在IDLE状态是是否可以正常运行,如果该appid合法且该appid不在设置的白名单中,该应用程序在IDLE状态时是不能运行的,将该wakeLock的disabled的值置为true

    如果该appid在白名单中,那么该程序在IDLE状态允许运行,将该wakeLock的disable状态置为false.

    private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) {
            if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
                    == PowerManager.PARTIAL_WAKE_LOCK) {
                boolean disabled = false;
                if (mDeviceIdleMode) {
                    final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
                    if (appid >= Process.FIRST_APPLICATION_UID &&
                            Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
                            Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 &&
                            mUidState.get(wakeLock.mOwnerUid,
                                    ActivityManager.PROCESS_STATE_CACHED_EMPTY)
                                    > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
                        disabled = true;
                    }
                }
                if (wakeLock.mDisabled != disabled) {
                    wakeLock.mDisabled = disabled;
                    return true;
                }
            }
            return false;
        }

    如果该wakeLock的disabled变量更新成功,当wakeLock的disabled的值为true,表示IDLE状态次wakeLock不可用,调用notifyWakeLockReleasedLocked(wakelock)通知释放该wakeLock。

    当wakeLock的disabled的值为false,表示IDLE状态此wakeLock可用,调用notifyWakeLockAcquiredLocked(wakeLock)通知获取该wakeLock。



    展开全文
  • Android中的Doze模式

    2021-02-07 15:01:34
    概述 Android 6.0引入的Doze机制在于节省系统耗电量,保护电池,延长电池的使用时间。当设备未连接至电源,且长时间处于闲置状态时,系统会将应用进入... 在进入Doze模式后,每间隔一段时间,会进入一段时长为30s的m

    概述

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

    Doze模式的状态

    该状态与API版本无关,未适配API23以上的应用只要运行在6.0以上的系统上就会受到Doze模式的影响。

    • 在屏幕熄灭30分钟、没有晃动并且在不充电的时候,会进入Doze模式
    • 在进入Doze模式后,每间隔一段时间,会进入一段时长为30s的maintenance window的窗口期,可以唤醒系统,进行网络交互等等
    • 进入Doze模式后,如果没有退出的话,系统唤醒的间隔时长会越来越长

    当系统处于Doze模式下,系统和白名单之外的应用将受到以下限制:

    • 无法访问网络
    • Wake Locks被忽略
    • AlarmManager闹铃会被推迟到下一个maintenance window响应
      • 使用setAndAllowWhileIdleSetExactAndAllowWhileIdle设置闹铃的闹钟则不会受到Doze模式的影响
      • setAlarmClock设置的闹铃在Doze模式下仍然生效,但系统会在闹铃生效前退出Doze
    • 系统不执行Wi-Fi/GPS扫描;
    • 系统不允许同步适配器运行;
    • 系统不允许JobScheduler运行;

    而位于白名单中的应用可以:

    • 继续使用网络并保留部分wake lock
    • Job和同步仍然会被推迟
    • 常规的AlarmManager闹铃也不会被触发

    应用申请加入白名单

    App可以通过PowerManager.isIgnoringBatteryOptimizations检查本App是否在系统的白名单列表中。

    如果不在,则可以通过在AndroidManifest.xml中添加REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限,并且通过发送ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS的Intent来向用户申请该权限

    原理

    Doze的原理是在框架层对资源加入了一层资源的调度。在监听系统硬件或者屏幕亮暗的中断信号所发出来的广播,然后对于JobScheduler以及AlarmManager中任务进行统一调度。

    而Doze的源码在于链接:
    /frameworks/base/services/core/java/com/android/server/DeviceIdleController.java

    DeviceIdleController中存在一个mState变量来保存当前设备的状态,状态值如下:



    作者:None_Ling
    链接:https://www.jianshu.com/p/d62d58d6ba5a
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

      /** Device is currently active. */
        private static final int STATE_ACTIVE = 0;
        /** Device is inactve (screen off, no motion) and we are waiting to for idle. */
        private static final int STATE_INACTIVE = 1;
        /** Device is past the initial inactive period, and waiting for the next idle period. */
        private static final int STATE_IDLE_PENDING = 2;
        /** Device is currently sensing motion. */
        private static final int STATE_SENSING = 3;
        /** Device is currently finding location (and may still be sensing). */
        private static final int STATE_LOCATING = 4;
        /** Device is in the idle state, trying to stay asleep as much as possible. */
        private static final int STATE_IDLE = 5;
        /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
        private static final int STATE_IDLE_MAINTENANCE = 6;

    DeviceIdleController继承自SystemService,在SystemServer初始化的时候,会初始化该对象,并且将它添加到ServiceManager

    而在onBootPhase,即设备Boot初始化阶段,也就是所有的SystemService都初始化完毕后,DeviceIdleController会初始化需要用到的AlarmManager、LocationManager等,并且会调用updateDisplayLoced

    @Override
        public void onBootPhase(int phase) {
            if (phase == PHASE_SYSTEM_SERVICES_READY) {
                synchronized (this) {
                    mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
                    mBatteryStats = BatteryStatsService.getService();
                    mLocalPowerManager = getLocalService(PowerManagerInternal.class);
                    mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                    mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
                    mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
                    mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
                    mLocationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
                    mLocationRequest = new LocationRequest().setQuality(LocationRequest.ACCURACY_FINE).setInterval(0).setFastestInterval(0).setNumUpdates(1);
                    mAnyMotionDetector = new AnyMotionDetector(
                            (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
                            mHandler, mSensorManager, this);
                    Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
                            .setPackage("android")
                            .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
    
                    Intent intentSensing = new Intent(ACTION_STEP_IDLE_STATE)
                            .setPackage("android")
                            .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    mSensingAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentSensing, 0);
                    mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                    mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY| Intent.FLAG_RECEIVER_FOREGROUND);
    
                    IntentFilter filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                    filter.addAction(ACTION_STEP_IDLE_STATE);
                    getContext().registerReceiver(mReceiver, filter);
    
                    mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                    mDisplayManager.registerDisplayListener(mDisplayListener, null);
                    updateDisplayLocked();
                }
            }
        }

    而在updateDisplayLockedupdateChargingLocked函数中会判断当前屏幕是否亮着,或者是否在充电,如果屏幕熄灭或者没在充电的话,则会调用becomeInactiveIfAppropriateLocked开始准备进入Doze状态。
    PS:后者是在收到ACTION_BATTERY_CHANGED的时候调用的,代表充电的变化

    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());
                }
            }
        }

    becomeInactiveIfAppropriateLocked函数中:

    • 将状态设置成STATE_INACTIVE
    • 取消定位、传感器监听的闹钟
    • 重新设置mInactiveTimeout时长的闹钟,也就是30分钟或者3分钟
    • 在闹钟的Intent中,会发送一个广播ACTION_STEP_IDLE_STATE
    void becomeInactiveIfAppropriateLocked() {
            if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
            if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) {
                // Screen has turned off; we are now going to become inactive and start
                // waiting to see if we will ultimately go idle.
                mState = STATE_INACTIVE;
                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                resetIdleManagementLocked();
                scheduleAlarmLocked(mInactiveTimeout, false);
                EventLogTags.writeDeviceIdle(mState, "no activity");
            }
        }
    
        void resetIdleManagementLocked() {
            mNextIdlePendingDelay = 0;
            mNextIdleDelay = 0;
            cancelAlarmLocked();
            cancelSensingAlarmLocked();
            cancelLocatingLocked();
            stopMonitoringSignificantMotion();
            mAnyMotionDetector.stop();
        }

    在接收到ACTION_STEP_IDLE_STATE的广播后,会调用stepIdleStateLocked,在该函数中,处理所有的状态变化,而在状态处理的过程中还会有几个Alarm被设置。在该函数中,主要涉及一些状态变化,以及闹钟的设置,借图说明:

    最终,在进入Doze模式后,会通过mHandler发送一个MSG_REPORT_IDLE_ON的消息,在该消息中,通过mNetworkPolicyManager.setDeviceIdleMode禁止网络连接,通过PowerManager来限制WakeLock

    case MSG_REPORT_IDLE_ON: {
                        EventLogTags.writeDeviceIdleOnStart();
                        mLocalPowerManager.setDeviceIdleMode(true);
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(true);
                            mBatteryStats.noteDeviceIdleMode(true, null, Process.myUid());
                        } catch (RemoteException e) {
                        }
                        getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                        EventLogTags.writeDeviceIdleOnComplete();

     

    展开全文
  • 当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络活动减少电量的消耗。App Stanbdy延缓用户最近没有使用app的后台网络活动。 作为移动开发人员,我们开发的App需要有推送功能,不希望在锁屏...
  • 对低电耗模式(app Standby)和应用待机模式(Doze)进行针对性优化 从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能,可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。低电耗模式...

    对低电耗模式(app Standby)和应用待机模式(Doze)进行针对性优化

    从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能,可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。低电耗模式通过在设备长时间处于闲置状态时推迟应用的后台 CPU 和网络 Activity 来减少电池消耗。应用待机模式可推迟用户近期未与之交互的应用的后台网络 Activity。

    低电耗模式和应用待机模式管理在 Android 6.0 或更高版本上运行的所有应用的行为,无论它们是否特别针对 API 级别 23。 为确保用户获得最佳体验,请在低电耗模式和应用待机模式下测试您的应用并对代码进行必要的调整。 以下部分介绍了详细信息。

    了解低电耗模式

    如果用户设备未插接电源、处于静止状态一段时间且屏幕关闭,设备会进入低电耗模式。 在低电耗模式下,系统会尝试通过限制应用对网络和 CPU 密集型服务的访问来节省电量。 这还可以阻止应用访问网络并推迟其作业、同步和标准闹铃。

    系统会定期退出低电耗模式一会儿,好让应用完成其已推迟的 Activity。在此维护时段内,系统会运行所有待定同步、作业和闹铃并允许应用访问网络。

    图 1. 低电耗模式提供了定期维护时段,可供应用使用网络并处理待定 Activity。

    在每个维护时段结束后,系统会再次进入低电耗模式,暂停网络访问并推迟作业、同步和闹铃。 随着时间的推移,系统安排维护时段的次数越来越少,这有助于在设备未连接至充电器的情况下长期处于不活动状态时降低电池消耗。

    一旦用户通过移动设备、打开屏幕或连接到充电器唤醒设备,系统就会立即退出低电耗模式,并且所有应用都将返回到正常 Activity。

    低电耗模式限制

    在低电耗模式下,您的应用会受到以下限制:

    将应用调整到低电耗模式

    低电耗模式可能会对应用产生不同程度的影响,具体取决于应用提供的功能和使用的服务。 许多应用无需修改即可在低电耗模式周期中正常运行。 在某些情况下,您必须优化应用管理网络、闹铃、作业和同步的方式。 应用应该能够在每个维护时段内高效管理 Activity。

    低电耗模式很容易影响 AlarmManager 闹铃和定时器管理的 Activity,因为当系统处于低电耗模式时,不会触发 Android 5.1(API 级别 22)或更低版本中的闹铃。

    为了帮助您安排闹铃,Android 6.0(API 级别 23)引入了两种新的 AlarmManager 方法:setAndAllowWhileIdle() 和 setExactAndAllowWhileIdle()。通过这些方法,您可以设置即使设备处于低电耗模式也会触发的闹铃。

    :对于每个应用,setAndAllowWhileIdle() 和 setExactAndAllowWhileIdle() 触发闹铃的时间间隔都不能超过 9 分钟。

    低电耗模式对网络访问的限制也有可能影响您的应用,特别是当应用依赖于操作消息 (tickle) 或通知等实时消息时更是如此。 如果应用需要持久连接到网络来接收消息,您应尽量使用 Google Cloud Messaging (GCM)

    要确认应用在低电耗模式下按照预期运行,您可以使用 adb 命令强制系统进入和退出低电耗模式并观察应用的行为。如需了解详细信息,请参阅在低电耗模式和应用待机模式下进行测试

    了解应用待机模式

    应用待机模式允许系统判定应用在用户未主动使用它时处于空闲状态。 当用户有一段时间未触摸应用时,系统便会作出此判定,以下条件均不适用:

    • 用户显式启动应用。
    • 应用当前有一个进程位于前台(表现为 Activity 或前台服务形式,或被另一 Activity 或前台服务占用)。
    • 应用生成用户可在锁屏或通知托盘中看到的通知。

    当用户将设备插入电源时,系统将从待机状态释放应用,从而让它们可以自由访问网络并执行任何待定作业和同步。 如果设备长时间处于空闲状态,系统将按每天大约一次的频率允许空闲应用访问网络。

    在设备空闲时使用 GCM 与您的应用交互

    Google Cloud Messaging (GCM) 是一项云端至设备的服务,允许您支持在后端服务与 Android 设备上的应用之间实时进行下游消息传递。GCM 提供了单一持久的云连接;所有需要实时传递消息的应用均可共享此连接。此共享连接使多个应用无需消耗电池即可维持自身单独的持久连接,避免快速耗尽电池,从而显著优化电池消耗。 因此,如果应用需要与后端服务进行消息传递集成,我们强烈建议您尽量使用 GCM,而非维持自身持久的网络连接。

    GCM 经过优化,可通过高优先级 GCM 消息用于低电耗模式和应用待机模式。GCM 高优先级消息允许您可靠地唤醒应用访问网络,即使用户设备处于低电耗模式或应用处于应用待机模式也不例外。 在低电耗模式或应用待机模式下,系统将传递消息并允许应用临时访问网络服务和部分唤醒锁,然后将设备或应用恢复到空闲状态。

    高优先级 GCM 消息不会影响低电耗模式,也不会影响任何其他应用的状态。这意味着您的应用可以使用这些消息进行有效的通信,同时尽可能减少对整个系统和设备的电池影响。

    作为一项常规最佳做法,如果您的应用需要下游消息传递,则应使用 GCM。如果您的服务器和客户端已经使用 GCM,请确保服务对关键消息使用高优先级消息,因为即使设备处于低电耗模式,这也会可靠地唤醒应用。

    对其他用例的支持

    通过妥善管理网络连接、闹铃、作业和同步并使用 GCM 高优先级消息,几乎所有应用都应该能够支持低电耗模式。对于一小部分用例,这可能还不够。 对于此类用例,系统为部分免除低电耗模式和应用待机模式优化的应用提供了一份可配置的白名单。

    在低电耗模式和应用待机模式期间,加入白名单的应用可以使用网络并保留部分 wake locks。 不过,正如其他应用一样,其他限制仍然适用于加入白名单的应用。 例如,加入白名单的应用的作业和同步将推迟(在 API 级别 23 及更低级别中),并且其常规 AlarmManager 闹铃不会触发。通过调用 isIgnoringBatteryOptimizations(),应用可以检查自身当前是否位于豁免白名单中。

    用户可以在 Settings > Battery > Battery Optimization 中手动配置该白名单。或者,系统会为应用提供请求用户将应用加入白名单的方式。

    在请求用户将应用添加到白名单之前,请确保应用符合加入白名单的可接受用例

    :除非应用的核心功能受到不利影响,否则 Google Play 政策禁止应用请求直接豁免 Android 6.0+ 中的电源管理功能(低电耗模式和应用待机模式)。

    在低电耗模式和应用待机模式下进行测试

    为了确保用户获得极佳体验,您应在低电耗模式和应用待机模式下全面测试您的应用。

    在低电耗模式下测试您的应用

    您可按以下步骤测试低电耗模式:

    1. 使用 Android 6.0(API 级别 23)或更高版本的系统映像配置硬件设备或虚拟设备。
    2. 将设备连接到开发计算机并安装应用
    3. 运行应用并使其保持活动状态
    4. 关闭设备屏幕。(应用保持活动状态。)
    5. 通过运行以下命令强制系统在低电耗模式之间循环切换:
      $ adb shell dumpsys battery unplug     $ adb shell dumpsys deviceidle step
    6. 您可能需要多次运行第二个命令。不断地重复,直到设备变为空闲状态。

    7. 在重新激活设备后观察应用的行为。确保应用在设备退出低电耗模式时正常恢复。

    在应用待机模式下测试您的应用

    要在应用待机模式下测试您的应用,请执行以下操作:

    1. 使用 Android 6.0(API 级别 23)或更高版本的系统映像配置硬件设备或虚拟设备。
    2. 将设备连接到开发计算机并安装应用
    3. 运行应用并使其保持活动状态
    4. 通过运行以下命令强制应用进入应用待机模式:
      $ adb shell dumpsys battery unplug $ adb shell am set-inactive <packageName> true
    5. 使用以下命令模拟唤醒应用:
      $ adb shell am set-inactive <packageName> false  $ adb shell am get-inactive <packageName>
    6. 观察唤醒后的应用行为。确保应用从待机模式中正常恢复。 特别地,您应检查应用的通知和后台作业是否按预期继续运行

    加入白名单的可接受用例

    下表重点介绍了请求加入或目前位于电池优化豁免白名单中的可接受用例。 通常情况下,除非低电耗模式或应用待机模式破坏了应用的核心功能,或者由于技术方面的原因而导致您的应用无法使用 GCM 高优先级消息,否则您的应用不应出现在白名单上。

    如需了解详细信息,请参阅对其他用例的支持

    类型用例是否可以使用 GCM?是否可接受加入白名单?备注
    即时通讯、聊天或通话应用。当设备处于低电耗模式或应用处于应用待机模式时,需要将实时消息传递给用户。是,可以使用 GCM不可接受应使用 GCM 高优先级消息唤醒应用并访问网络。
    是,但并非使用 GCM 高优先级消息。
    即时通讯、聊天或通话应用;企业 VOIP 应用。否,由于在技术上依赖其他消息传递服务,或者低电耗模式和应用待机模式破坏了应用的核心功能,因此不能使用 GCM。可接受 
    任务自动化应用应用的核心功能是安排自动化操作,例如即时通讯、语音通话、新照片管理或位置操作。如果适用。可接受 
    外围设备协同应用应用的核心功能是保持与外围设备的持久连接,以便为外围设备提供互联网访问权限。如果适用。可接受 
    应用只需定期连接到外围设备进行同步,或者只需连接到设备,例如通过标准蓝牙配置文件连接的无线耳机。如果适用。不可接受

    Android Doze模式启用和恢复详解:
    一、模拟未充电状态
    模拟手机在连接USB的状态下的未充电状态。
    使用adb命令
    我们先用 adb shell dumpsys battery 
    这段命令查看一下手机当前的状态,可以看到当前手机是连接USB充电

    敲入命令 adb shell dumpsys battery unplug 
    模拟手机未充电状态
    这时我们看到 AC powered 和 USB powered 都已经关闭,证明模拟手机未充电状态成功。

    二、IDLE有效化
    敲入命令 adb shell dumpsys deviceidle enable 让IDLE有效化。

    三、进入IDLE模式方法
    有两种方法。
    第一种是,屏幕亮着状态按下电源按钮关闭屏幕,敲入命令让其进入IDLE模式。
    (注意:我们在切换状态的时候要重复输入几次命令,直到进入IDLE模式。)
    我们可以清晰地看到,手机在重复执行几次之后已经进入IDLE模式。证明第一种方法好用。

    第二种是,敲入命令强制让手机进入IDLE模式。
    敲入命令 adb shell dumpsys deviceidle force-idle
    我们可以敲入命令查看并验证当前手机是否已经进入IDLE模式
    敲入命令 adb shell dumpsys deviceidle
    我们可以看到手机屏幕当前是开启状态,但是已经进入IDLE模式。证明第二种方法也好用。

    四、查看Doze模式白名单
    adb shell dumpsys deviceidle whitelist 
    把应用加入电池优化白名单的shell命令:
    adb shell dumpsys deviceidle whitelist +<packageName>
    退出白名单即优化该应用的shell命令为:
    adb shell dumpsys deviceidle whitelist -<packageName>

    五、恢复手机状态
    当我们模拟完状态之后要恢复回去,以便手机能够正常使用。
    敲入命令 adb shell dumpsys deviceidle disable 和 adb shell dumpsys battery reset 即可让手机恢复状态。
    再敲入 adb shell dumpsys battery 查看一下手机是否已经恢复状态。
    我们可以看到,手机已经从IDLE模式恢复到正常状态。

    Standby应用,也就是休眠应用(可以理解为墓碑机制,我这么理解的)累死绿色守护的浅睡模式。
    以休眠QQ为例子,具体命令:
    adb shell dumpsys battery unplug
    adb shell am set-inactive com.tencent.mobileqq true

    展开全文
  • Android Doze模式模拟 从Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户。用户管理可以在没有充电的情况下管理app的行为。当用户一段时间没有使用手机的时候,Doze模式通过...

    Android Doze模式模拟

    从Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户。用户管理可以在没有充电的情况下管理app的行为。当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络活动减少电量的消耗。App Stanbdy延缓用户最近没有使用app的后台网络活动。

    作为移动开发人员,我们开发的App需要有推送功能,不希望在锁屏或者不充电的时候被Doze模式干掉。那么如何检测手机进入Doze模式之后App的状态呢?

     

    一、模拟未充电状态

    模拟手机在连接USB的状态下的未充电状态。

    使用adb命令

    我们先用 adb shell dumpsys battery 这段命令查看一下手机当前的状态

    可以看到当前手机是连接USB充电

    敲入命令 adb shell dumpsys battery unplug 模拟手机未充电状态

    这时我们看到 AC powered 和 USB powered 都已经关闭,证明模拟手机未充电状态成功。

     

    二、IDLE有效化

    敲入命令 adb shell dumpsys deviceidle enable 让IDLE有效化。

     

    三、进入IDLE模式方法

    有两种方法。

    第一种是,屏幕亮着状态按下电源按钮关闭屏幕,敲入命令让其进入IDLE模式。

    (注意:我们在切换状态的时候要重复输入几次命令,直到进入IDLE模式。)

    我们可以清晰地看到,手机在重复执行几次之后已经进入IDLE模式。证明第一种方法好用。

     

    第二种是,敲入命令强制让手机进入IDLE模式。

    敲入命令 adb shell dumpsys deviceidle force-idle

    我们可以敲入命令查看并验证当前手机是否已经进入IDLE模式

    敲入命令 adb shell dumpsys deviceidle

    我们可以看到手机屏幕当前是开启状态,但是已经进入IDLE模式。证明第二种方法也好用。

     

    四、恢复手机状态

    当我们模拟完状态之后要恢复回去,以便手机能够正常使用。

    敲入命令 adb shell dumpsys deviceidle disable 和 adb shell dumpsys battery reset 即可让手机恢复状态。再敲入 adb shell dumpsys battery 查看一下手机是否已经恢复状态。

    我们可以看到,手机已经从IDLE模式恢复到正常状态。

    展开全文
  • Android Doze模式调试

    千次阅读 2017-05-02 15:48:39
    在没有充电的情况下,当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络活动减少电量的消耗。App Stanbdy延缓用户最近没有使用app的后台网络活动。 今天主要检测下手机进入Doze模式之后App的...
  • Doze的退出,说的更严格一点,就是当Doze模式的状态由其他状态变为ACTIVE状态。简而言之,退出Doze模式有三种情况:屏幕亮屏、插入充电器、设备有移动。下面就这三种情况进行下分析。 在前面的分析中我们有见到过...
  • 进入到IDLE状态(可用adb shell dumpsys deviceidle命令查看:mState=IDLE) 至此,手机进入到深度休眠模式。 如果手机有TYPE_SIGNIFICANT_MOTION运动触发传感器,用力左右晃动,手机会进入到INACTIVE状态。即退出...
  • AndroidDoze模式识别与检测

    千次阅读 2019-05-27 10:33:56
    Android 6.0(API 级别 23)开始,Android 引入了两个省电功能:Doze模式(官方翻译为低电耗模式)和 App Standby模式(官方翻译为应用待机模式),可通过管理应用在设备未连接至电源时的行为方式为用户延长电池...
  • Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户。...当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络活动减少电量的消耗。App S...
  • Android doze模式分析

    2017-11-08 17:15:44
    http://blog.csdn.net/q1183345443/article/details/61922910 http://blog.csdn.net/q1183345443/article/details/62042906
  • androidP Doze模式 androidP 添加 doze白名单 省电优化白名单 通过命令查找文件 grep allow-in-power-save frameworks/base/ --include=*.xml -Rn 配置文件路径 frameworks/base/data/etc/platform.xml 配置说明 ...
  • Android O Doze模式的状态

    千次阅读 2017-11-18 19:26:53
    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 ...
  • 在第一篇Android 8.1 Doze模式分析(一)我们知道,如果设备处于未充电且屏幕关闭状态,就会进入Light Doze模式,在LightDoze模式中,会定期进行维护,这种维护会持续N分钟,在维护状态(maintenance)时,会进行网络...
  • Android doze模式分析一

    千次阅读 2018-11-25 00:58:28
    DOZE安卓系统从6.0(API级别23)开始引入的对低电和应用待机模式的优化。具体介绍可以参考developers官方文档。 阅读完官文后,思考几个问题: 6.0之前,APP怎么处理可以让系统不休眠? 6.0开始,系统如何解决APP不...
  • Android 8.1 Doze模式分析(一)

    千次阅读 2018-12-27 17:56:37
    Doze模式(低电耗模式),是Andoriod6.0增加的一项系统服务,主要目的是为了优化电池性能,增加电池续航时间,Doze模式又分两种模式:深度Doze模式(Deep Doze)和轻度Doze模式(Light Doze),如果用户长时...
  • Android系统休眠机制和doze模式的关系

    千次阅读 2017-08-22 14:09:49
    Android系统的休眠机制和doze模式
  • Android Doze模式

    千次阅读 2016-11-30 10:33:30
    当设备屏幕关闭,不充电,Doze模式会通过限制app访问网络,推迟后台作业,同步来减少了对于电池电量的消耗。对于一些不是经常使用的app,Appstandby会禁止app后台的网络活动。 只要App在android6.0或更高版本的系
  • 为了克服android OREO(targetSdkVersion 27)后台服务限制,我创建了一个带有Notification的前台服务 . 通知显示在通知栏中,到目前为止服务正在运行 . 我在前台服务类中使用FusedLocationClient将我的代码与活动识别...
  • Android DOZE模式

    2018-08-31 16:51:45
    Android 6.0(API 级别 23)引入了打盹模式,当用户设备未插接电源、处于静止状态且屏幕关闭时,该模式会推迟 CPU 和网络活动,从而延长电池寿命。而 Android N 则通过在设备未插接电源且屏幕关闭状态下、但不一定要...
  • Doze模式简介

    千次阅读 2021-02-17 23:29:34
    Doze模式是自Android 6.0开始引入的两项省电功能的其中之一,还有一个就是appstandby,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 ...
  • While the device is dozing, syncs and jobs are turned off globally, ... When the device is in idle maintenance mode, all of the doze restrictions are removed (so AllowWhileIdle can happen once a minute).
  • LightDoze表示轻度doze模式,如果设备处于未充电且屏幕关闭状态,但未处于静止状态时,就会进入Light Doze模式,在LightDoze模式中,会定期进行维护,这种维护会持续N分钟,在维护状态(maintenance)时,会进行网络的...
  • Android7.0 Doze模式

    万次阅读 2016-11-07 15:30:56
    Android M中,Google就引入了Doze模式。它定义了一种全新的、低能耗的状态。 在该状态,后台只有部分任务被允许运行,其它任务都被强制停止。本篇博客中,我们就来分析一下Android 7.0中Doze模式相关的流程。
  • 激活Alarm的Doze模式 void scheduleAlarmLocked(long delay, boolean idleUntil) { if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")"); // NOTE: Bug #627645 low power ...
  • Deep Doze,也就是AndroidDoze模式了,表示深度Doze,比起LightDoze,它将进行更多的限制:无法进行网络访问和 GPS/WLAN 扫描、唤醒被忽略、闹钟和作业/同步被延迟。当然,它的触发条件也将更加苛刻:灭屏、未充电...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,756
精华内容 702
关键字:

安卓doze模式