精华内容
下载资源
问答
  • 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,置于App Standby模式。而最终的效果,能提升30%的电量续航能力...

    概述

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

     

    展开全文
  • Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户。用户管理可以在没有充电的情况下管理app的行为。当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络...
  • Doze的退出,说的更严格一点,就是当Doze模式的状态由其他状态变为ACTIVE状态。简而言之,退出Doze模式有三种情况:屏幕亮屏、插入充电器、设备有移动。下面就这三种情况进行下分析。 在前面的分析中我们有见到过...

    概述

    Doze的退出,说的更严格一点,就是当Doze模式的状态由其他状态变为ACTIVE状态。简而言之,退出Doze模式有三种情况:屏幕亮屏、插入充电器、设备有移动。下面就这三种情况进行下分析。

    在前面的分析中我们有见到过becomeActiveLocked()方法,这个方法当时没有进行分析,这个方法就是用来退出Doze的,严格来说,是将Doze状态置为了ACTIVE状态,从而退出IDLE状态或MAINTENANCE状态。因此,不管是何种方式退出Doze,都会调用这个方法。该方法如下:

    void becomeActiveLocked(String activeReason, int activeUid) {
        if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
            //设置一个Handler,在Handler中通知PMS、发送idle changed 广播
            scheduleReportActiveLocked(activeReason, activeUid);
            //将DeepDoze状态值置为STATE_ACTIVE
            mState = STATE_ACTIVE;
            //将LightDoze状态值置为LIGHT_STATE_ACTIVE
            mLightState = LIGHT_STATE_ACTIVE;
            //多久后进入INACTIVE状态
            mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
            mCurIdleBudget = 0;
            mMaintenanceStartTime = 0;
            //重置DeepDoze相关属性值、alarm、listener等
            resetIdleManagementLocked();
            //重置LightDoze相关属性值、alarm、listener等
            resetLightIdleManagementLocked();
        }
    }
    

    1.屏幕亮灭屏

    当亮屏或灭屏时,PMS发送亮灭屏广播,DIC中监听了亮灭屏的广播,负责接受该广播并进行屏幕状态改变后的操作:
    注册广播:

    filter = new IntentFilter();
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_SCREEN_ON);
    getContext().registerReceiver(mInteractivityReceiver, filter);
    

    接收到广播后进行处理:

    private final BroadcastReceiver mInteractivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (DeviceIdleController.this) {
                updateInteractivityLocked();
            }
        }
    };
    

    updateInteractivityLocked()方法中更新LightDoze和DeepDoze的状态,这个方法在第一篇文章中分析过了。

    2.充电状态改变

    当电池状态时,在BatteryService中会发送广播,DIC也对该广播进行了监听:

    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    getContext().registerReceiver(mReceiver, filter);
    

    接收到广播后进行处理:

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                ............
                case Intent.ACTION_BATTERY_CHANGED: {
                    synchronized (DeviceIdleController.this) {
                        int plugged = intent.getIntExtra("plugged", 0);
                        updateChargingLocked(plugged != 0);
                    }
                } break;
                ....................
            }
        }
    };
    

    当接收到该广播后,调用updateChargingLocked(plugged != 0)来更新状态,参数表示是否插有充电线,该方法如下:

    void updateChargingLocked(boolean charging) {
        //由充电转变为放电
        if (!charging && mCharging) {
            mCharging = false;
            if (!mForceIdle) {
                //进入INACTIVE状态,开始Doze模式
                becomeInactiveIfAppropriateLocked();
            }
        } else if (charging) {//由放电转变为充电
            mCharging = charging;
            if (!mForceIdle) {
                //Doze状态退出ACTIVE状态,退出Doze模式
                becomeActiveLocked("charging", Process.myUid());
            }
        }
    }
    

    最终,也会调用becomeActiveLocked()退出Doze模式。

    3.其他服务通过Binder调用

    DeviceIdleController内部Binder也提供了用于退出Doze的接口:

    @Override public void exitIdle(String reason) {
        getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
                null);
        long ident = Binder.clearCallingIdentity();
        try {
            exitIdleInternal(reason);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
    

    其内部也是通过调用becomeActiveLocked()方法退出Doze的:

    
    public void exitIdleInternal(String reason) {
        synchronized (this) {
            becomeActiveLocked(reason, Binder.getCallingUid());
        }
    }
    

    4.MotionSensor检测状态发生

    这种情况和以上几种情况略不同,这种情况下Doze的退出是瞬时的,会将两个状态值都转换为STATE_ACTIVE后,又会立即调用becomeInactiveIfAppropriateLocked()方法进入INACTIVE状态,可以说是个“瞬时”退出。当MotionSensor检测到有移动时,则会退出Doze,处理逻辑在handleMotionDetectedLocked()中:

    void handleMotionDetectedLocked(long timeout, String type) {
        boolean becomeInactive = false;
        if (mState != STATE_ACTIVE) {
            //通过Handler通知PMS、NetworkPolicy、发送IDLE_CHANGED广播等
            scheduleReportActiveLocked(type, Process.myUid());
            mState = STATE_ACTIVE;//将DeepDoze状态变为ACTIVE状态
            mInactiveTimeout = timeout;
            mCurIdleBudget = 0;
            mMaintenanceStartTime = 0;
            becomeInactive = true;
        }
        if (mLightState == LIGHT_STATE_OVERRIDE) {
            mLightState = STATE_ACTIVE;//将LightDoze状态变为ACTIVE状态
            becomeInactive = true;
        }
        if (becomeInactive) {
            //进入INACTIVE状态
            becomeInactiveIfAppropriateLocked();
        }
    }
    
    展开全文
  • 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 嗨,您好! 我们是Navigine团队。 八年来,我们一直在提供集成的定位移动技术,以实现先进的室内导航和邻近解决方案。 今天,我们决定为我们的技术敞开大门,并讨论在Android应用程序被杀死并处于后台...
  • Android Doze模式调试

    千次阅读 2017-05-02 15:48:39
    Android 6.0开始,Android提供两种延长电池寿命的功能 doze和 App Standby。在没有充电的情况下,当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络活动减少电量的消耗。App Stanbdy延缓用户...
  • 对低电耗模式(app Standby)和应用待机模式(Doze)进行针对性优化 从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能,可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。低电耗模式...
  • 1.Doze模式下豁免的应用和白名单列表 1.什么是白名单列表? 一系列应用包名的集合。 2.有什么作用? 处于白名单列表中的应用,不受Doze的影响,即Doze不会对该应用进行限制,如该应用的Job、...
  • Android doze模式分析

    2017-11-08 17:15:44
    http://blog.csdn.net/q1183345443/article/details/61922910 http://blog.csdn.net/q1183345443/article/details/62042906
  • Android系统休眠机制和doze模式的关系

    千次阅读 2017-08-22 14:09:49
    Android系统的休眠机制和doze模式
  • Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户。用户管理可以在没有充电的情况下管理app的行为。当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的CPU和网络...
  • 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).
  • 安卓Doze 相关命令

    2018-01-16 15:35:37
    1.强制进入Doze模式 dumpsys deviceidle force-idle 2.启用Doze模式 dumpsys deviceidle enable 3.关闭Doze模式 dumpsys deviceidle disable 4.查看Doze模式白名单 dumpsys deviceidle whitelist
  • Android Doze模式使用命令启用和恢复

    千次阅读 2017-11-06 14:29:29
    1,用 adb shell dumpsys battery 这段命令查看一下手机当前的状态; --->USB powered:true 2,用adb shell dumpsys battery unplug 模拟手机未充电状态; 再敲入(1.)的命令可以看到 --->USB powered:false ...
  • androidP Doze模式 androidP 添加 doze白名单 省电优化白名单 通过命令查找文件 grep allow-in-power-save frameworks/base/ --include=*.xml -Rn 配置文件路径 frameworks/base/data/etc/platform.xml 配置说明 ...
  • Deep Doze,也就是AndroidDoze模式了,表示深度Doze,比起LightDoze,它将进行更多的限制:无法进行网络访问和 GPS/WLAN 扫描、唤醒被忽略、闹钟和作业/同步被延迟。当然,它的触发条件也将更加苛刻:灭屏、未充电...
  • Android doze模式分析一

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

    千次阅读 2019-05-27 10:33:56
    Android 6.0(API 级别 23)开始,Android 引入了两个省电功能:Doze模式(官方翻译为低电耗模式)和 App Standby模式(官方翻译为应用待机模式),可通过管理应用在设备未连接至电源时的行为方式为用户延长电池...
  • Android O 的Doze模式白名单路径Doze 模式列表上述备注规则如下if(powerWhitelist.isSysWhitelisted(pkg)) { // Summary of app which doesn't have a battery optimization setting show:Battery optimization ...
  • android 6.0开始,谷歌引入了两项新的省电技术延长电池使用时间,分别是Doze(休眠)和App Standby(app待命模式),只要app是运行在6.0(api 23)及以上的系统,无论app编译时是否使用的target=23,都会受到这两...
  • android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), callingUid, "AlarmManager.set"); } // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_...
  • Android M中引入的两个重要的功能是Doze和Appstandby,目的是延长设备的电池寿命。 根据文档,实现前台服务的应用程序可以被App Standby豁免,但是没有关于Doze的类似说法。 M发布几个月后,很快就推出了大量设备...
  • 为了克服android OREO(targetSdkVersion 27)后台服务限制,我创建了一个带有Notification的前台服务 . 通知显示在通知栏中,到目前为止服务正在运行 . 我在前台服务类中使用FusedLocationClient将我的代码与活动识别...
  • 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 Doze模式

    千次阅读 2016-11-30 10:33:30
    android6.0开始,android引入了两种省电技术以延长电池的使用寿命,分别是低电耗模式(Doze)和应用待机模式(App standby)模式。当设备屏幕关闭,不充电,Doze模式会通过限制app访问网络,推迟后台作业,同步来...

空空如也

空空如也

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

安卓doze