Doze模式简介

article/2025/9/15 11:35:12

Doze模式是自Android 6.0开始引入的两项省电功能的其中之一,还有一个就是appstandby,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 CPU 和网络活动,从而降低耗电量。应用待机模式会延迟用户近期未与之交互的应用的后台网络活动。

当设备处于低电耗模式时,应用对某些高耗电量资源的访问会延迟到维护期。如果用户未插接设备的电源,在屏幕关闭的情况下,让设备在一段时间内保持不活动状态,那么设备就会进入低电耗模式。在低电耗模式下,系统会尝试通过限制应用访问占用大量网络和 CPU 资源的服务来节省电量。它还会阻止应用访问网络,并延迟其作业、同步和标准闹钟。系统会定期退出低电耗模式一小段时间,让应用完成其延迟的活动。在此维护期内,系统会运行所有待处理的同步、作业和闹钟,并允许应用访问网络。

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

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

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

  1. 暂停访问网络
  2. 系统忽略唤醒锁定
  3. 标准 Ala'r'm'Manager闹钟(包括 setExact() 和 setWindow())推迟到下一个维护期  如果您需要设置在设备处于低电耗模式时触发的闹钟,请使用 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()使用 setAlarmClock() 设置的闹钟将继续正常触发,系统会在这些闹钟触发之前不久退出低电耗模式)
  4. 系统不执行 WLAN 扫描
  5. 系统不允许运行同步适配器
  6. 系统不允许运行 JobScheduler

测试相关:

运行以下命令,强制系统进入doze模式

adb shell dumpsys deviceidle force-idle

准备就绪后,运行以下命令,使系统退出闲置模式

adb shell dumpsys deviceidle unforce

执行以下命令,重新激活设备

adb shell dumpsys battery reset

以上介绍内容来自:

https://developer.android.com/topic/performance?hl=zh-cn

 

代码实现简介(基于android R):

Doze模式的控制代码都在DeviceIdleController中,DeviceIdleController作为一个系统服务在systemserver中启动,启动后监听电池广播/应用卸载广播/屏幕亮灭广播,还通过注册传感器监听来获知当前设备是否在请求未知信息,是否有运动,来确定当前设备状态,是否需要进入doze模式。进入doze模式后通过NetworkPolicyManagerService来修改联网策略,通过BatteryStatsService修改电池省电策略,通过PowerManagerService来修改持锁策略,且通过发送广播DEVICE_IDLE_MODE_CHANGED来通知对应的省电模块和jobschedule模块做出相应调整。

Doze模式状态图如下:

Doze模式状态流转如下图:

DeviceIdleController根据不同状态做不同的检测,设置不同的闹钟时间间隔,在最后强制设备进入doze睡眠模式,且会在进入后一段时间自动的退出doze模式,短暂退出后会再次进入,且距离下次退出doze模式时间会逐次拉长。在任意一个非STATA_ACTIVE状态下,如果接收到亮屏或者充电广播,或者是注册的传感器监听到运动信息,都会直接将状态回退到STATE_ACTIVE状态,所以进入doze模式的条件就是在灭屏不充电状态下经过一段时间的静止后才会进入,这个时间通过系统设置。

DeviceIdleController的运行机制主要依赖一下四个模块:

  1. 数据模块Constants,提供了基础数据,比如各个不同状态直接闹钟的间隔时间
  2. 广播监听,监听电池变化,亮灭屏,应用卸载,网络变化广播,来确定进入或者退出doze状态模式流转的时机
  3. 传感器监听,通过监听传感器,获知当前设备活动情况
  4. 闹钟,各状态间切换,doze模式的开放窗口期进入都是通过闹钟来操作

 

几个比较重要的方法如下:

初始化:

主要的初始化工作都在onBootPhase和onStart方法中完成,这里主要对一些需要用大的服务做初始化,广播注册,数据初始化,白名单初始化

    @Overridepublic void onBootPhase(int phase) {Slog.d(TAG, "onBootPhase phase = " + phase);if (phase == PHASE_SYSTEM_SERVICES_READY) {synchronized (this) {//各种需要用的的服务管理类的初始化mAlarmManager = mInjector.getAlarmManager();mLocalAlarmManager = getLocalService(AlarmManagerInternal.class);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");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));mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);mSensorManager = mInjector.getSensorManager();if (mUseMotionSensor) {mMotionSensor = mInjector.getMotionSensor();}mAppStateTracker.onSystemServicesReady();......//广播和广播接收器的初始化和注册mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);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.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);mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,state -> {synchronized (DeviceIdleController.this) {updateQuickDozeFlagLocked(state.batterySaverEnabled);}});updateQuickDozeFlagLocked(mLocalPowerManager.getLowPowerState(ServiceType.QUICK_DOZE).batterySaverEnabled);mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);passWhiteListsToForceAppStandbyTrackerLocked();updateInteractivityLocked();}//根据联网状态判断light模式状态updateConnectivityState(null);}}
    @Overridepublic void onStart() {final PackageManager pm = getContext().getPackageManager();synchronized (this) {mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(com.android.internal.R.bool.config_enableAutoPowerModes);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,PackageManager.MATCH_SYSTEM_ONLY);int appid = UserHandle.getAppId(ai.uid);mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);} catch (PackageManager.NameNotFoundException e) {}}ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();for (int i=0; i<allowPower.size(); 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;mLightState = LIGHT_STATE_ACTIVE;mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;mPreIdleFactor = 1.0f;mLastPreIdleFactor = 1.0f;}mBinderService = new BinderService();publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);mLocalService = new LocalService();publishLocalService(DeviceIdleInternal.class, mLocalService);}

电池与卸载广播接收器:

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Override public void onReceive(Context context, Intent intent) {switch (intent.getAction()) {case ConnectivityManager.CONNECTIVITY_ACTION: {updateConnectivityState(intent);} break;case Intent.ACTION_BATTERY_CHANGED: {boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;synchronized (DeviceIdleController.this) {updateChargingLocked(present && plugged);}} break;case Intent.ACTION_PACKAGE_REMOVED: {if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {Uri data = intent.getData();String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {removePowerSaveWhitelistAppInternal(ssp);}}} break;}}};

两灭屏广播:

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

不管是电池变化的广播中调用的updateChargingLocked方法还是亮灭屏广播中的updateInteractivityLocked方法,最终都调用了两个方法:

进入不活跃状态becomeInactiveIfAppropriateLocked,进入活跃状态becomeActiveLocked

    void becomeInactiveIfAppropriateLocked() {verifyAlarmStateLocked();final boolean isScreenBlockingInactive =mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked);if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {return;}// Become inactive and determine if we will ultimately go idle.if (mDeepEnabled) {if (mQuickDozeActivated) {if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE|| mState == STATE_IDLE_MAINTENANCE) {// Already "idling". Don't want to restart the process.// mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3// values, so returning here is safe.return;}if (DEBUG) {Slog.d(TAG, "Moved from "+ stateToString(mState) + " to STATE_QUICK_DOZE_DELAY");}mState = STATE_QUICK_DOZE_DELAY;// Make sure any motion sensing or locating is stopped.resetIdleManagementLocked();if (isUpcomingAlarmClock()) {scheduleAlarmLocked(mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()+ mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);} else {scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);}EventLogTags.writeDeviceIdle(mState, "no activity");} else if (mState == STATE_ACTIVE) {mState = STATE_INACTIVE;if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");resetIdleManagementLocked();long delay = mInactiveTimeout;if (shouldUseIdleTimeoutFactorLocked()) {delay = (long) (mPreIdleFactor * delay);}if (isUpcomingAlarmClock()) {scheduleAlarmLocked(mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()+ delay, false);} else {scheduleAlarmLocked(delay, false);}EventLogTags.writeDeviceIdle(mState, "no activity");}}if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {mLightState = LIGHT_STATE_INACTIVE;if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");resetLightIdleManagementLocked();scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);EventLogTags.writeDeviceIdleLight(mLightState, "no activity");}}

becomeInactiveIfAppropriateLocked方法主要功能就是设置进入下一状态的闹钟

    private void becomeActiveLocked(String activeReason, int activeUid,long newInactiveTimeout, boolean changeLightIdle) {if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason);mState = STATE_ACTIVE;mInactiveTimeout = newInactiveTimeout;resetIdleManagementLocked();// Don't reset maintenance window start time if we're in a light idle maintenance window// because its used in the light idle budget calculation.if (mLightState != LIGHT_STATE_IDLE_MAINTENANCE) {mMaintenanceStartTime = 0;}if (changeLightIdle) {EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason);mLightState = LIGHT_STATE_ACTIVE;resetLightIdleManagementLocked();// Only report active if light is also ACTIVE.scheduleReportActiveLocked(activeReason, activeUid);addEvent(EVENT_NORMAL, activeReason);}}}

becomeActiveLocked的功能是将状态设置回active状态,并且取消所有设置的进入下一状态的闹钟,取消行为监听

doze模式light与deep两种模式之间各个状态之间转换在stepIdleStateLocked与stepLightIdleStateLocked方法中实现,这里详细介绍下stepIdleStateLocked

    @VisibleForTestingvoid stepIdleStateLocked(String reason) {......switch (mState) {case STATE_INACTIVE:// We have now been inactive long enough, it is time to start looking// for motion and sleep some more while doing so.startMonitoringMotionLocked();long delay = mConstants.IDLE_AFTER_INACTIVE_TIMEOUT;if (shouldUseIdleTimeoutFactorLocked()) {delay = (long) (mPreIdleFactor * delay);}scheduleAlarmLocked(delay, false);moveToStateLocked(STATE_IDLE_PENDING, reason);break;case STATE_IDLE_PENDING:moveToStateLocked(STATE_SENSING, reason);cancelLocatingLocked();mLocated = false;mLastGenericLocation = null;mLastGpsLocation = null;updateActiveConstraintsLocked();// Wait for open constraints and an accelerometer reading before moving on.if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);mNotMoving = false;mAnyMotionDetector.checkForAnyMotion();break;} else if (mNumBlockingConstraints != 0) {cancelAlarmLocked();break;}mNotMoving = true;// Otherwise, fall through and check this off the list of requirements.case STATE_SENSING:cancelSensingTimeoutAlarmLocked();moveToStateLocked(STATE_LOCATING, reason);scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);LocationManager locationManager = mInjector.getLocationManager();if (locationManager != null&& locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {locationManager.requestLocationUpdates(mLocationRequest,mGenericLocationListener, mHandler.getLooper());mLocating = true;} else {mHasNetworkLocation = false;}if (locationManager != null&& locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {mHasGps = true;locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,mGpsLocationListener, mHandler.getLooper());mLocating = true;} else {mHasGps = false;}// If we have a location provider, we're all set, the listeners will move state// forward.if (mLocating) {break;}// Otherwise, we have to move from locating into idle maintenance.case STATE_LOCATING:cancelAlarmLocked();cancelLocatingLocked();mAnyMotionDetector.stop();// Intentional fallthrough -- time to go into IDLE state.case STATE_QUICK_DOZE_DELAY:// Reset the upcoming idle delays.mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;mNextIdleDelay = mConstants.IDLE_TIMEOUT;// Everything is in place to go into IDLE state.case STATE_IDLE_MAINTENANCE:scheduleAlarmLocked(mNextIdleDelay, true);if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +" ms.");mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);mIdleStartTime = SystemClock.elapsedRealtime();mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {mNextIdleDelay = mConstants.IDLE_TIMEOUT;}moveToStateLocked(STATE_IDLE, reason);if (mLightState != LIGHT_STATE_OVERRIDE) {mLightState = LIGHT_STATE_OVERRIDE;cancelLightAlarmLocked();}addEvent(EVENT_DEEP_IDLE, null);mGoingIdleWakeLock.acquire();mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);break;case STATE_IDLE:// We have been idling long enough, now it is time to do some work.mActiveIdleOpCount = 1;mActiveIdleWakeLock.acquire();scheduleAlarmLocked(mNextIdlePendingDelay, false);if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +"Next alarm in " + mNextIdlePendingDelay + " ms.");mMaintenanceStartTime = SystemClock.elapsedRealtime();mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;}moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);addEvent(EVENT_DEEP_MAINTENANCE, null);mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);break;}}

从这里可以清晰的看到doze模式是怎么一步步深入检测的,从息屏断电到进入doze模式,需要经过多次闹钟等待,这期间不能有gps请求或者设备活动,否则会直接回退到最初的active状态,进入doze模式后通过mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);

        @Override public void handleMessage(Message msg) {switch (msg.what) {case MSG_REPORT_IDLE_ON:case MSG_REPORT_IDLE_ON_LIGHT: {// mGoingIdleWakeLock is held at this pointEventLogTags.writeDeviceIdleOnStart();final boolean deepChanged;final boolean lightChanged;if (msg.what == MSG_REPORT_IDLE_ON) {deepChanged = mLocalPowerManager.setDeviceIdleMode(true);lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);} else {deepChanged = mLocalPowerManager.setDeviceIdleMode(false);lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);}try {mNetworkPolicyManager.setDeviceIdleMode(true);mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON? BatteryStats.DEVICE_IDLE_MODE_DEEP: BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());} catch (RemoteException e) {}if (deepChanged) {getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);}if (lightChanged) {getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);}EventLogTags.writeDeviceIdleOnComplete();mGoingIdleWakeLock.release();} break;

进入后通过设置NetworkPolicyManagerService和BatteryStatsService的标志位来修改应用的联网策略和电视省电策略,并且发送广播通知其他模块进入了doze模式,

其中广播会在DeviceIdleJobsController中对待执行的jobschedler进行延迟操作。

                case MSG_REPORT_IDLE_OFF: {// mActiveIdleWakeLock is held at this pointEventLogTags.writeDeviceIdleOffStart("unknown");final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);try {mNetworkPolicyManager.setDeviceIdleMode(false);mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,null, Process.myUid());} catch (RemoteException e) {}if (deepChanged) {incActiveIdleOps();getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL,null, mIdleStartedDoneReceiver, null, 0, null, null);}if (lightChanged) {incActiveIdleOps();getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,null, mIdleStartedDoneReceiver, null, 0, null, null);}// Always start with one active op for the message being sent here.// Now we are done!decActiveIdleOps();EventLogTags.writeDeviceIdleOffComplete();} break;

进入maintenance状态所进行的操作和进入idle状态的操作正好相反,将NetworkPolicyManagerService和BatteryStatsService的标志位设置为false,发送广播通知idle状态短暂的结束。被延迟的job会被执行,此时应用也可以访问网络。

至此DeviceIdleController的主要功能就大体分析完成,从谷歌官网提供的信息来看doze模式功能十分强大,但是DeviceIdleController却只是起到一个负责doze的进入与退出,通过一些列的条件判断是否需要进入doze模式,一旦进入真正的各种省电策略都是通过其他模块来具体协助实现,比如联网策略控制,电池状态控制,job任务控制,以进一步减少设备在不活动期间后台电量消耗,达到省电的目地。

 


http://chatgpt.dhexx.cn/article/AhqtNg6g.shtml

相关文章

Android中的Doze模式

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

Mock工具之Mockito实战

在实际项目中写单元测试的过程中我们会发现需要测试的类有很多依赖&#xff0c;这些依赖项又会有依赖&#xff0c;导致在单元测试代码里几乎无法完成构建&#xff0c;尤其是当依赖项尚未构建完成时会导致单元测试无法进行。为了解决这类问题我们引入了Mock的概念&#xff0c;简…

Mockito 入门

目录 1.什么是 Mock 测试&#xff1f;2.Mockito简介3.在 SpringBoot 单元测试中使用 Mockito3.1 Maven依赖&#xff1a;3.2 UserService.java3.3 User.java3.4 thenReturn系列方法&#xff08;测试桩&#xff09;3.5 thenThrow系列方法3.6 verify 系列方法 4.Spring中mock任何容…

Mock工具之Moco使用教程

目录 一、什么是Moco二、安装&配置三、接口配置&测试3.1 第一个简单的请求&#xff1a;3.2 Get请求&#xff1a;3.3 Post请求&#xff1a;3.4 返回值为Json格式的请求&#xff1a;3.5 带cookie信息才能访问的post请求&#xff1a;3.6 重定向请求&#xff1a;3.7 前后端…

mock功能

目标 了解mock的作用及使用场景&#xff1b; mock使用场景 mock&#xff1a;假的 前端程序员提到的mock数据的含义是&#xff1a;真的假数据 真的&#xff1a;符合接口规范要求的。 假数据&#xff1a;数据是人为创建出来的&#xff0c;不是真正的业务数据。 什么时候需…

mock的介绍

1.什么是mock mock测试是以可控的方式模拟真实的对象行为。程序员通常创造模拟对象来测试对象本身该具备的行为&#xff0c;很类似汽车设计者使用碰撞测试假人来模拟车辆碰撞中人的动态行为 2.为什么要使用mock 之所以使用mock测试&#xff0c;是因为真实场景很难实现或者短…

Mock介绍

mock的定义&#xff08;what&#xff09;&#xff1a; mock是在测试过程中&#xff0c;对于一些不容易构造/获取的对象&#xff0c;创建一个mock对象来模拟对象的行为 为什么要使用mock&#xff08;why&#xff09;&#xff1a; 在做单元测试过程中&#xff0c;经常会有以下的…

Mockito单元测试

文章目录 Mockito单元测试为什么要使用Mock?导入依赖import导入包使用Mock模拟测试某个类中的某个方法是否可以成功执行使用Mock模拟某个类的方法&#xff0c;自己给这个方法返回我们指定的值使用Mock模拟某个方法调用后会抛出指定的异常使用Mock模拟测试某个类中的某个方法(加…

javascript 中怎么判断为数字类型

javascript中判断变量是否为数字类型抄的方法有两种&#xff1a; 方法一&#xff1a; function isnum(s) { if(s!null){ var r,re; re /\d*/i; //\d表示数字,*表示匹配多个数字 r s.match(re); return (rs)?true:false; } return false; } 方法二&#xff1a; function isnu…

js判断字符串是不是一个纯数字

1.使用隐式转换判断 使用parseInt()/parseFloat()会把其他类型转换为数值&#xff0c;转换结果为数值或NaN,转换字符串的时候&#xff0c;头部内容有数值就会截取前面&#xff0c;如123456aff789bb转成123456&#xff0c;后面即使有数字也不会转换处理&#xff0c;开头如果是字…

javaScript 判断是否为数值类型的三种方法

第一种方法:isNaN(value) isNaN : isNaN() 函数用于检查其参数是否是非数字值。 返回true则判断为非数值。缺点&#xff1a;空字符串&#xff0c;空格 &#xff0c;null会做为数字0进行处理&#xff0c;结果为falsehttps://www.w3school.com.cn/js/jsref_isnan.anusp 第二种方法…

JS中判断变量是否为数字方法

推荐教程&#xff1a;《JavaScript视频教程》 JavaScript 是一种动态类型语言&#xff0c;这意味着解释器在运行时确定变量的类型。实际上&#xff0c;这也允许我们在相同的代码中使用相同的变量来存储不同类型的数据。如果没有文档和一致性&#xff0c;我们在使用代码时并不总…

matlab绘制图形中,常用函数调用(num2str,disp,gcf,hold on,plot,axis,subplot,line,stairs,grid,set,gca)

num2str 将数字转换为字符数组 s = num2str(A) 将数字数组转换为表示数字的字符数组。输出格式取决于原始值的大小。 num2str对于使用数值标注和标题绘图非常有用。 s = num2str(A,precision) 返回一个字符数组,表示具有精度指定的最大有效位数的数字。 s = num2str(A,fo…

Matlab中num2str函数使用

目录 语法 说明 示例 浮点值的默认转换 指定精度 指定格式 num2str函数将数字转换为字符数组。 语法 s num2str(A)s num2str(A,precision)s num2str(A,formatSpec) 说明 s num2str(A) 将数值数组转换为表示数字的字符数组。输出格式取决于原始值的量级。num2str 对…

C/C++ split函数 num2str str2num函数

C/C split函数,num2str及str2num函数实现 在C98及其以下版本的C/C中&#xff0c;没有现成的字符串split函数和num2str及str2num函数&#xff0c;对于竞赛不能使用C11的我来说非常头疼&#xff0c;通过学习stringstream和strtok实现了自己的split函数&#xff0c;通过stringstr…

matlab 中num2str函数的使用

运行ECO代码报错&#xff1a;转义字符 ‘\d’ 无效。有关支持的特殊字符&#xff0c;请参阅 ‘doc sprintf’。 num2str函数功能&#xff1a;将数字转换为字符串 具体所有功能可见官网。 这里使用的格式为&#xff1a; str num2str(A, format) 将 format 指定的格式应用到 A …

js基础运用——js用*打印三角形,菱形

js基础运用 新手见解&#xff0c;有不足或更简单的方法希望各位指点 1.用*排列直角三形 &#xff08;图一&#xff09; &#xff08;图二&#xff09; 图一图二是js语句利用*号画出来的直角三角形&#xff0c;方法思路都一致&#xff0c;下面以图一为例讲一下本人所用的方法…

java:打印十字图

java:打印十字图 题目 问题描述 小明为某机构设计了一个十字型的徽标&#xff08;并非红十字会啊&#xff09;&#xff0c;如下所示&#xff1a;..$$$$$$$$$$$$$.. ..$...........$.. $$$.$$$$$$$$$.$$$ $...$.......$...$ $.$$$.$$$$$.$$$.$ $.$...$...$...$.$ $.$.$$$.$.$$…

JAVA 利用JFram,JPanel,JScrollPane简单文本框实现

****1.JFram 支持通用窗口的所有基本功能 2…JPanel是一个无边框&#xff0c;不可以被移动的&#xff0c;放大的&#xff0c;缩小的&#xff0c;或者关闭的面板&#xff0c;默认布局是FlowLayout&#xff08;也可用setLayout&#xff08;&#xff09;来设定&#xff09;** 3……

deflater java_Java Deflater finished()用法及代码示例

如果已到达压缩数据输出流的末尾&#xff0c;则java.util.zip中Deflater类的finished()函数将返回true。 函数签名&#xff1a; public boolean finished() 用法: d.finished(); 参数&#xff1a;该函数不需要参数 返回类型&#xff1a;该函数返回布尔值&#xff0c;即如果所有…