前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android中的Doze模式

Android中的Doze模式

作者头像
None_Ling
发布2019-07-10 17:25:33
3.2K0
发布2019-07-10 17:25:33
举报
文章被收录于专栏:Android相关

概述

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

Doze模式的状态

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

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

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变量来保存当前设备的状态,状态值如下:

代码语言:javascript
复制
    /** 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

DeviceIdleController

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

代码语言:javascript
复制
@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的时候调用的,代表充电的变化

代码语言:javascript
复制
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
代码语言:javascript
复制
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状态变化

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

代码语言:javascript
复制
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();
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.07.08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • Doze模式的状态
  • 应用申请加入白名单
  • 原理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档