Doze模式详解

Doze模式详解

Doze模式,官方翻译为低电耗模式,Doze 英译为 打盹、打瞌睡

先从官方文档来看看它是干嘛的(要养成阅读源码和官方文档的习惯)

https://developer.android.com/training/monitoring-device-state/doze-standby

根据官方文档。

从 Android 6.0(API 级别 23)开始,Android 引入了两项省电功能,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 CPU 和网络活动,从而降低耗电量。应用待机模式会延迟用户近期未与之交互的应用的后台网络活动。

当设备处于低电耗模式时,应用对某些高耗电量资源的访问会延迟到维护期。电源管理限制中列出了具体的限制。

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

可以得知Doze模式就是一个节能省电的模式,分为Light Doze和Deep Doze。

Light Doze (轻度Doze) : 灭屏但仍处于移动状态时,会进入的模式。

Deep Doze (深度Doze): 灭屏且处于静止状态时,会进入的模式。

• Doze模式期间,会周而复始的重复【 休眠 à 维护 à 休眠 】 的状态,且每进入下一次休眠都会延长休眠的时长。

• 通过延缓/限制应用后台cpu和网络等活动 (在Doze模式的维护态中统一进行),从而达到省电的目的。

本文主要关注三点。

1- 服务的启动流程。

2- 介绍一下doze模式的工作流程

3- Doze模式与其他模块的交互。

首先分析,服务的启动流程:

代码分析基于Android Q

base\services\java\com\android\server\SystemServer.java

Android框架服务都是在systemserver里面启动的,Doze 也不例外

systemServer在startOtherServices时,将 DeviceIdleController 启动起来

mSystemServiceManager.startService(DeviceIdleController.class);

DeviceIdleController就是doze 服务的真身。

startService 这个方法本质是通过反射获取到 DeviceIdleController 的构造函数,然后调用构造函数造了这个对象,构造完了之后调用父类定义的onStart方法。

分析构造函数

public DeviceIdleController(Context context) {

this(context, new Injector(context));

}

@VisibleForTesting DeviceIdleController(Context context, Injector injector) {

super(context);

mInjector = injector;

mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));

mHandler = mInjector.getHandler(this);

mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());

LocalServices.addService(AppStateTracker.class, mAppStateTracker);

mUseMotionSensor = mInjector.useMotionSensor();

}

1- 构造一个 injector 辅助类,这个辅助类只是封装了一些系统资源服务的获取方法 (injector的翻译是注射器 =_= )。

2- 读取 SystemDir 目录下 的配置文件 deviceidle.xml。

3- 调用辅助类初始化mHandler,这里用到的 looper 是BackgroundThread 线程的。

4- 初始化 mAppStateTracker

5- 将服务加载到 LocalServices中。

6- 注册MotionSensor 。这个sensor判断手机有没有动。

看下这个类名

public class DeviceIdleController extends SystemService

implements AnyMotionDetector.DeviceIdleCallback {

它是继承自 SystemService,表明这是一个系统服务

同时实现了 AnyMotionDetector 的接口,这个接口是判断手机是否静止的。

可见官方注释的介绍

/**

* Determines if the device has been set upon a stationary object.

*/

接下来就是调用父类的onStart方法

@Override

public void onStart() {

final PackageManager pm = getContext().getPackageManager();//获取pm,很奇怪,这里为啥不用injector?统一不好吗!!

synchronized (this) {

mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(

com.android.internal.R.bool.config_enableAutoPowerModes);

SystemConfig sysConfig = SystemConfig.getInstance();//获取系统配置文件

ArraySet allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();//获取非idle白名单

for (int i=0; i

String pkg = allowPowerExceptIdle.valueAt(i);

try {

ApplicationInfo ai = pm.getApplicationInfo(pkg,

PackageManager.MATCH_SYSTEM_ONLY);

int appid = UserHandle.getAppId(ai.uid);

mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);//将白名单加入低电模式非idle白名单

mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);

} catch (PackageManager.NameNotFoundException e) {

}

}

ArraySet allowPower = sysConfig.getAllowInPowerSave();//获取白名单,这个是纯白名单

for (int i=0; i

String pkg = allowPower.valueAt(i);

try {

ApplicationInfo ai = pm.getApplicationInfo(pkg,

PackageManager.MATCH_SYSTEM_ONLY);

int appid = UserHandle.getAppId(ai.uid);

// These apps are on both the whitelist-except-idle as well

// as the full whitelist, so they apply in all cases.

mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);//先加入不纯的白名单

mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);

mPowerSaveWhitelistApps.put(ai.packageName, appid);//再加入纯的白名单

mPowerSaveWhitelistSystemAppIds.put(appid, true);

} catch (PackageManager.NameNotFoundException e) {

}

}

mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());//初始化常量中心

readConfigFileLocked();//这里是解析刚才加载的配置文件,里面为用户白名单应用

updateWhitelistAppIdsLocked();//更新白名单

mNetworkConnected = true;//初始化各个状态,网络连接

mScreenOn = true;//亮灭屏

mScreenLocked = false;//锁屏

// Start out assuming we are charging. If we aren't, we will at least get

// a battery update the next time the level drops.

mCharging = true;//充电

mActiveReason = ACTIVE_REASON_UNKNOWN;//唤醒原因

mState = STATE_ACTIVE;//这个是DeepDoze模式的状态

mLightState = LIGHT_STATE_ACTIVE;//LightDoze模式的状态

mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;//超时的实际

mPreIdleFactor = 1.0f;

mLastPreIdleFactor = 1.0f;

}

mBinderService = new BinderService();//binder连接服务端

publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);//发布服务,提供跨进程访问

publishLocalService(LocalService.class, new LocalService());//发布到本地

}

主要就是

1- 解析获取系统白名单、用户应用白名单,保存下来。

2- 初始化一些有用的状态,充放电,亮灭屏。

3- 发布服务,一个是本地,一个是Binder提供跨进程访问。

systemServer 启动完各个服务之后,就会调用 startBootPhase

mSystemServiceManager.startBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);

public void startBootPhase(final int phase) {

if (phase <= mCurrentPhase) {

throw new IllegalArgumentException("Next phase must be larger than previous");

}

mCurrentPhase = phase;

Slog.i(TAG, "Starting phase " + mCurrentPhase);

try {

Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "OnBootPhase " + phase);

final int serviceLen = mServices.size();

for (int i = 0; i < serviceLen; i++) {

final SystemService service = mServices.get(i);

long time = SystemClock.elapsedRealtime();

Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, service.getClass().getName());

try {

service.onBootPhase(mCurrentPhase);

} catch (Exception ex) {

throw new RuntimeException("Failed to boot service "

+ service.getClass().getName()

+ ": onBootPhase threw an exception during phase "

+ mCurrentPhase, ex);

}

warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onBootPhase");

Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

}

} finally {

Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

}

}

看实现方法,这里就是调用了各个service 的onBootPhase,跟踪进入 DeviceIdleController 的 onBootPhase。

@Override

public void onBootPhase(int phase) {

if (phase == PHASE_SYSTEM_SERVICES_READY) {

synchronized (this) {

mAlarmManager = mInjector.getAlarmManager();//获取ALMS

mLocalAlarmManager = getLocalService(AlarmManagerInternal.class);//获取本地AlarmManager

mBatteryStats = BatteryStatsService.getService();//同理了

mLocalActivityManager = getLocalService(ActivityManagerInternal.class);

mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);

mLocalPowerManager = getLocalService(PowerManagerInternal.class);

mPowerManager = mInjector.getPowerManager();

mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,//申请一个deviceidle_maint锁

"deviceidle_maint");

mActiveIdleWakeLock.setReferenceCounted(false);

mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,//再申请一个锁

"deviceidle_going_idle");

mGoingIdleWakeLock.setReferenceCounted(true);

mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(

ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));//获取mNetworkPolicyManager服务的代理端

mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);//获取本地服务

mSensorManager = mInjector.getSensorManager();//调用注射器获取服务

if (mUseMotionSensor) { //获取sensor

mMotionSensor = mInjector.getMotionSensor();

}

if (getContext().getResources().getBoolean(//啥玩意这是?

com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {

mLocationRequest = new LocationRequest()

.setQuality(LocationRequest.ACCURACY_FINE)

.setInterval(0)

.setFastestInterval(0)

.setNumUpdates(1);

}

mConstraintController = mInjector.getConstraintController(

mHandler, getLocalService(LocalService.class));

if (mConstraintController != null) {

mConstraintController.start();//电视设备的idle限制>>>

}

float angleThreshold = getContext().getResources().getInteger(

com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;

mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,

angleThreshold);

mAppStateTracker.onSystemServicesReady();//跟踪强制应用待机相关的类

mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);//初始化两个intent

mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY

| Intent.FLAG_RECEIVER_FOREGROUND);

mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);

mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY

| Intent.FLAG_RECEIVER_FOREGROUND);

IntentFilter filter = new IntentFilter();//初始化filter

filter.addAction(Intent.ACTION_BATTERY_CHANGED);//充电放电变化的广播

getContext().registerReceiver(mReceiver, filter);

filter = new IntentFilter();

filter.addAction(Intent.ACTION_PACKAGE_REMOVED);//卸载应用的广播

filter.addDataScheme("package");

getContext().registerReceiver(mReceiver, filter);

filter = new IntentFilter();

filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);//连接网络的广播

getContext().registerReceiver(mReceiver, filter);

filter = new IntentFilter();

filter.addAction(Intent.ACTION_SCREEN_OFF);//亮灭屏

filter.addAction(Intent.ACTION_SCREEN_ON);

getContext().registerReceiver(mInteractivityReceiver, filter);//注册广播接收器

mLocalActivityManager.setDeviceIdleWhitelist(

mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);//将白名单给ATMS(AMS)

mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);//将白名单给PowerManagerService

mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,

state -> {

synchronized (DeviceIdleController.this) {

updateQuickDozeFlagLocked(state.batterySaverEnabled);

}

});//监听低电模式

updateQuickDozeFlagLocked(

mLocalPowerManager.getLowPowerState(

ServiceType.QUICK_DOZE).batterySaverEnabled);

mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);

passWhiteListsToForceAppStandbyTrackerLocked();

updateInteractivityLocked();//更新状态,跑起来!

}

updateConnectivityState(null);//更新网络连接态?

}

}

终于启动完成,可以跑流程了!

2- 介绍一下Doze模式的工作流程

主要流程大概就是

1- 获取一堆系统管理服务(AMS,PowerMS,NetWork)

2- 获取MotionSensor,监听设备运动状态

3- 注册广播接收器,充放电、亮灭屏、卸载应用、网络连接

Doze模式可以分为 LightDoze 和 DeepDoze,这两个是分开跑的。用两个不同的变量来标识状态。

private int mState; // DeepDoze

private int mLightState; // LightDoze

其中,轻度doze状态有7中情况

/** Device is currently active. */

@VisibleForTesting

static final int LIGHT_STATE_ACTIVE = 0;//活跃态

/** Device is inactive (screen off) and we are waiting to for the first light idle. */

@VisibleForTesting

static final int LIGHT_STATE_INACTIVE = 1;//不活跃态,灭屏了,正在等待进入第一个Light idle

/** Device is about to go idle for the first time, wait for current work to complete. */

@VisibleForTesting

static final int LIGHT_STATE_PRE_IDLE = 3;//前置IDLE态,等当前工作完成了就要第一次进入idle态

/** Device is in the light idle state, trying to stay asleep as much as possible. */

@VisibleForTesting

static final int LIGHT_STATE_IDLE = 4;//IDLE态

/** Device is in the light idle state, we want to go in to idle maintenance but are

* waiting for network connectivity before doing so. */

@VisibleForTesting

static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;//设备在idle态,我们想进入维护态,但是得先等网连上

/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */

@VisibleForTesting

static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;//维护态,短暂退出idle态,做一些常规维护动作

/** Device light idle state is overriden, now applying deep doze state. */

@VisibleForTesting

static final int LIGHT_STATE_OVERRIDE = 7;//无效态,设备light doze被覆盖了,进入 deep doze

有没有发现2不见了?很神奇是不是?其实这是为了和DeepDoze对应起来。

DeepDoze有

/** Device is currently active. */

@VisibleForTesting

static final int STATE_ACTIVE = 0;//活跃态

/** Device is inactive (screen off, no motion) and we are waiting to for idle. */

@VisibleForTesting

static final int STATE_INACTIVE = 1;//非活跃态,灭屏,没动,等待进入IDLE态

/** Device is past the initial inactive period, and waiting for the next idle period. */

@VisibleForTesting

static final int STATE_IDLE_PENDING = 2;//等待IDLE态,经过非活跃态,等待进入下一个IDle态

/** Device is currently sensing motion. */

@VisibleForTesting

static final int STATE_SENSING = 3;//感应态,正在感应运动

/** Device is currently finding location (and may still be sensing). */

@VisibleForTesting

static final int STATE_LOCATING = 4;//定位态,设备正在定位

/** Device is in the idle state, trying to stay asleep as much as possible. */

@VisibleForTesting

static final int STATE_IDLE = 5;//IDLE态

/** Device is in the idle state, but temporarily out of idle to do regular maintenance. */

@VisibleForTesting

static final int STATE_IDLE_MAINTENANCE = 6;//维护态,在IDLE期间进入维护态时,会使设备正常工作运行

/**

* Device is inactive and should go straight into idle (foregoing motion and location

* monitoring), but allow some time for current work to complete first.

*/

Doze模式触发

低电耗模式限制

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

暂停访问网络。

系统忽略唤醒锁定。 (忽略 wakelock)

标准 [AlarmManager](https://developer.android.com/reference/android/app/AlarmManager) 闹钟(包括 [setExact()](https://developer.android.com/reference/android/app/AlarmManager#setExact(int,%20long,%20android.app.PendingIntent)) 和 [setWindow()](https://developer.android.com/reference/android/app/AlarmManager#setWindow(int,%20long,%20long,%20android.app.PendingIntent)))推迟到下一个维护期。

如果您需要设置在设备处于低电耗模式时触发的闹钟,请使用 setAndAllowWhileIdle() 或 [setExactAndAllowWhileIdle()](https://developer.android.com/reference/android/app/AlarmManager#setExactAndAllowWhileIdle(int,%20long,%20android.app.PendingIntent))。

使用 [setAlarmClock()](https://developer.android.com/reference/android/app/AlarmManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo,%20android.app.PendingIntent)) 设置的闹钟将继续正常触发,系统会在这些闹钟触发之前不久退出低电耗模式。

系统不执行 WLAN 扫描。

系统不允许运行同步适配器。

系统不允许运行 [JobScheduler](https://developer.android.com/reference/android/app/job/JobScheduler)。

相关推荐

最新,955神仙公司名单(完整版)
best365怎么登

最新,955神仙公司名单(完整版)

📅 10-29 👁️ 8359
如何顺利办理国外银行卡注销手续?那些你不能不知道的事!
如何高效制作工作表格?这些技巧你一定要知道!