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
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 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)。