Android 8.1 Doze模式分析(一)——Doze简介和DeviceIdleController的启动

article/2025/9/15 11:31:04

概述

Doze模式,官方翻译为低电耗模式,是Andoriod6.0增加的一项系统服务,主要目的是为了优化电池性能,增加电池续航时间,Doze模式又分两种模式:深度Doze模式(Deep Doze)和轻度Doze模式(Light Doze),如果用户长时间没有主动使用其设备,处于静止状态且屏幕已关闭,则系统会使设备进入Doze模式,也就是深度Doze模式。如果用户关闭设备屏幕但仍处于移动状态时,则设备进入轻度Doze模式,此外,轻度Doze模式只适合Android7.0及以上版本。

当用户长时间未使用设备时,设备进入Doze模式,Doze模式会延迟应用后台 CPU 和网络活动,从而延长电池续航时间。处于Doze模式的设备会定期进入维护时段,在此期间,应用可以完成待进行的活动。然后,Doze模式会使设备重新进入较长时间的休眠状态,接着进入下一个维护时段。在达到几个小时的休眠时间上限之前,平台会周而复始地重复Doze模式休眠/维护的序列,且每一次都会延长Doze模式时长。处于Doze模式的设备始终可以感知到动作,且会在检测到动作时立即退出Doze模式。整个Doze图示如下:
这里写图片描述

Deep Doze 和Light Doze模式对比如下:

操作低电耗模式轻度低电耗模式
触发因素屏幕关闭、电池供电、静止屏幕关闭、电池供电(未插电)
时间随维护时段依次增加随维护时段反复持续 N 分钟
限制无法进行网络访问、唤醒锁忽略、 GPS/WLAN无法扫描、闹钟和SyncAdapter/JobScheduler被延迟。无法进行网络访问、SyncAdapter/JobScheduler
行为仅接收优先级较高的推送通知消息。接收所有实时消息(即时消息、来电等)。优先级较高的推送通知消息可以暂时访问网络。
退出设备有移动、和用户有交互、屏幕开启、闹钟响铃屏幕开启。

接下来就分析Doze的实现原理。Doze模式是通过DeviceIdleController来实现的。

1.DeviceIdleController的启动流程

DeviceIdleController(以下简称DIC)继承于SystemService,因此也是一个系统服务,在分析PMS的时候说过,继承于SytemService的服务启动有以下几个共同点:

  • 1.在SystemServer中实例化并启动,启动时会执行SytemService的生命周期方法:
    Constructor()->onStart()->onBootPhase()
  • 2.内部维护一个Binder和其他服务进行IPC通讯;
  • 3.内部维护一个Internal类用于和System进程进行交互;

下面就从SystemServer开始分析DIC的启动流程。

在SystemServer启动其他服务时启动DIC:

private void startOtherServices() {.............
mSystemServiceManager.startService(DeviceIdleController.class);
............
}

在SystemServiceManager中,通过反射的方式获取了DIC对象,并且调用了onStart()方法:

public <T extends SystemService> T startService(Class<T> serviceClass) {try {....................final String name = serviceClass.getName();Slog.i(TAG, "Starting " + name);final T service;try {//通过反射获取实例Constructor<T> constructor = serviceClass.getConstructor(Context.class);service = constructor.newInstance(mContext);} catch (InstantiationException ex) {}//调用同名重载方法startService(service);return service;} ....................
}
public void startService(@NonNull final SystemService service) {// 加入到mServices列表中mServices.add(service);long time = SystemClock.elapsedRealtime();try {//调用onStart()开始服务service.onStart();} catch (RuntimeException ex) {}
}

执行到这里,DIC的启动就开始了,再来看看最后一个生命周期方法onBootPhase()的调用,这个方法表示启动服务的过程,在SystemServer中会调用多次,从而在不同的启动阶段完成不同的工作,代码如下:

private void startOtherServices() {.............mSystemServiceManager.startBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);.............mSystemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);.............
mSystemServiceManager.startBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);.............mSystemServiceManager.startBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);.............
}

在SystemServiceManager中的startBootPhase()方法中,遍历已启动的服务列表,调用onBootPhase():

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 {for (int i = 0; i < serviceLen; i++) {final SystemService service = mServices.get(i);long time = SystemClock.elapsedRealtime();try {//调用每个service的onBootPhase()service.onBootPhase(mCurrentPhase);} catch (Exception ex) {}}} 
}

当这个方法执行完毕后,DIC的启动就完成了,下面我们看看DIC的生命周期方法中做了什么。首先看其构造方法:

public DeviceIdleController(Context context) {super(context);//创建可以执行原子操作的文件,/data/system/deviceidle.xmlmConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));//创建Handler,和BackgroundThread的Looper进行绑定mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
}

构造方法中,首先创建在/data/sytem下创建了deviceidle.xml文件,然后创建了一个Handler,并将BackgroundThread中Handler的Looper和该Handler进行绑定。BackgroundThread是一个单例模式实现的后台线程,可以用于任何一个进程。

再来看看DIC的onStart()方法:

@Override
public void onStart() {final PackageManager pm = getContext().getPackageManager();synchronized (this) {//Light doze模式和Deep Doze是否可用,默认不可用mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(com.android.internal.R.bool.config_enableAutoPowerModes);//获取系统全局配置信息,如权限SystemConfig sysConfig = SystemConfig.getInstance();//从配置文件中读取省电模式下的白名单列表且不在在idle状态的白名单列表,即列表中的app能够在省电模式下在后台运行,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);//添加到Map中,表示这些应用在省电模式下可后台运行,但在Doze下后台不可运行mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);//添加到SpareArray中,表示这是处于powersave白名单但不处于doze白名单的系统应用                mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);} catch (PackageManager.NameNotFoundException e) {}}//从配置文件中读取时能够在省电模式白名单列表,即包括不在doze白名单中的应用,也包括所有的白名单上的应用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);mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);//添加到DIC中的白名单列表中mPowerSaveWhitelistApps.put(ai.packageName, appid);//添加到DIC中的系统应用白名单列表中mPowerSaveWhitelistSystemAppIds.put(appid, true);} catch (PackageManager.NameNotFoundException e) {}}//设置Contants内容观察者mConstants = new Constants(mHandler, getContext().getContentResolver());//读取/data/system/deviceidle.xml文件,将读取的app添加到表示用户设置的白名单中readConfigFileLocked();//更新白名单列表updateWhitelistAppIdsLocked();//网络是否连接mNetworkConnected = true;//屏幕是否保持常亮mScreenOn = true;// 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;mState = STATE_ACTIVE;//设备保持活动状态,深度Doze的初始值mLightState = LIGHT_STATE_ACTIVE;//设备保持活动状态,轻度Doze的初始值mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;}mBinderService = new BinderService();//发布远程服务publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);//发布本地服务publishLocalService(LocalService.class, new LocalService());
}

在onStart()方法中,首先通过SystemConfig读取了两类白名单列表:在低电量模式下后台允许运行的应用的白名单、在低电量模式和Doze模式下都允许后台运行的应用白名单;
然后读取配置文件/data/system/deviceidle.xml,将读取的应用包名添加到mPowerSaveWhiteListUserApps这个Map中,表示用户添加到白名单中的应用。接下来更新白名单列表,然后给成员变量进行初始化。
最后,发布了远程服务和本地服务,从而可以和其他服务进行交互。发布后其他服务中就可以通过这种获取到DIC的BinderService从而进行交互:

IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));

获取本地服务通过如下方式:

mLocalDeviceIdleController= LocalServices.getService(DeviceIdleController.LocalService.class);

在onStart()方法中还调用了用来更新白名单列表的方法,来看下这个方法:


private void updateWhitelistAppIdsLocked() {//处于省电模式白名单但不处于Idle状态白名单的appmPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(mPowerSaveWhitelistAppsExceptIdle,mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);//处于所有的白名单的app,包括用户添加的mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);//用户添加的白名单列表应用mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);if (mLocalActivityManager != null) {//将适用于所有情况的白名单列表通知给AMSmLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);}if (mLocalPowerManager != null) {//将适用于所有情况的白名单列表通知给PMSmLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);}if (mLocalAlarmManager != null) {//将用户添加到白名单列表中的应用通知给AlarmManagerServicemLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);}
}

在这个方法中,会将三类白名单列表中的应用id添加到一个int数组中,该方法在添加应用到白名单列表、将应用移除白名单列表,都会调用该方法来更新白名单列表。

执行完onStart()方法后,就开始执行最后一个生命周期方法onBootPhase()方法了,该方法如下:


@Override
public void onBootPhase(int phase) {if (phase == PHASE_SYSTEM_SERVICES_READY) {synchronized (this) {mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);mBatteryStats = BatteryStatsService.getService();mLocalActivityManager = getLocalService(ActivityManagerInternal.class);mLocalPowerManager = getLocalService(PowerManagerInternal.class);mPowerManager = getContext().getSystemService(PowerManager.class);//申请一个wakelock锁保持CPU唤醒mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"deviceidle_maint");//设置wakelock锁为非计数锁,只要执行一次release()就能释所有非计数锁mActiveIdleWakeLock.setReferenceCounted(false);mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"deviceidle_going_idle");//设置wakelock锁为计数锁,一次申请对应一次释放mGoingIdleWakeLock.setReferenceCounted(true);mConnectivityService = (ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE);mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));mSensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);//可用于自动省电模式时的传感器id,0表示没有可用传感器int sigMotionSensorId = getContext().getResources().getInteger(com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);if (sigMotionSensorId > 0) {//根据传感器id获取传感器mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);}//如果没有指定任一传感器&&在没有指定传感器情况下首选WristTilt//传感器配置为trueif (mMotionSensor == null && getContext().getResources().getBoolean(com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {//获取一个WristTilt传感器(手腕抖动)mMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_WRIST_TILT_GESTURE, true);}if (mMotionSensor == null) {//如果以上条件都不满足,则获取一个SMD传感器mMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION, true);}//是否在进入Doze模式时预先获取位置if (getContext().getResources().getBoolean(com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {mLocationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);mLocationRequest = new LocationRequest().setQuality(LocationRequest.ACCURACY_FINE).setInterval(0).setFastestInterval(0).setNumUpdates(1);}//自动省电模式下传感器检测的阈值度float angleThreshold = getContext().getResources().getInteger(com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;//用于检测设备是否已静止mAnyMotionDetector = new AnyMotionDetector((PowerManager) getContext().getSystemService(Context.POWER_SERVICE),mHandler, mSensorManager, this, angleThreshold);//用于Doze状态发生改变时发送广播mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY| Intent.FLAG_RECEIVER_FOREGROUND);//用于当轻度Doze状态发生改变时发送广播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);//将适用于所有情况的白名单列表通知给AMS、PMS、AlarmManagerServicemLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray)//更新交互状态updateInteractivityLocked();}//更新网络连接状态updateConnectivityState(null);} else if (phase == PHASE_BOOT_COMPLETED) {}
}

首先,只有在SystemServer中启动阶段为PHASE_SYSTEM_SERVICE_READY时,才会进入到onBootPhase()方法,也就是说,DIC的onBootPhase()方法在到达这一启动阶段时才执行。在onBootPhase()方法中,获取了一些系统服务管理类和用于System进程的本地服务,如AlarmManager、PowerManagerInternal等,用于和它们对应的系统服务进行交互;
其次获取一个Sensor,具体获取哪种类型的Sensor根据判断条件而定;接下来获取了一个AnyMotionDector对象,该对象用来检测设备是否处于禁止状态;然后注册了四种广播的监听,分别是:

  • 1.电源状态发生改变时的广播:Intent.ACTION_BATTERY_CHANGED,由BatteryService中发送。
  • 2.卸载应用时的广播:Intent.ACTION_PACKAGE_REMOVED,由PKMS发送;
  • 3.网络连接状态改变时的广播:ConnectivityManager.CONNECTIVITY_ACTION
  • 4亮灭屏时的广播:Intent.SCREEN_ON/SCREEN_OFF,由PMS相关的Notifier发送;

最后,调用了一个用于更新交互状态的updateInteractivityLocked()方法和用于更新网络状态的updateConnectivityState()方法。先看看updateInteractivityLocked()方法:

void updateInteractivityLocked() {// The interactivity state from the power manager tells us whether the display is// in a state that we need to keep things running so they will update at a normal// frequency.//获取设备是否处于交互状态boolean screenOn = mPowerManager.isInteractive();if (DEBUG) Slog.d(TAG, "updateInteractivityLocked: screenOn=" + screenOn);//表示当前不处于交互状态且上次处于交互状态if (!screenOn && mScreenOn) {mScreenOn = false;if (!mForceIdle) {//是否强制进入Idle//进入Idle模式的入口方法becomeInactiveIfAppropriateLocked();}} else if (screenOn) {mScreenOn = true;if (!mForceIdle) {//退出Idle模式becomeActiveLocked("screen", Process.myUid());}}
}

这个方法在Android 8.0及以前,是updateDisplayLocked(),在8.1中,修改了名字。这个方法中通过设备当前的交互状态,决定是否进入Doze模式或者退出Doze模式,如果当前处于不交互状态(PMS中分析过,wakefulness=asleep或者wakefulness=doze),且上次处于交互状态,则会调用becomeInteractiveIfAppropriateLocked()方法,开始准备进入Doze的工作;如果条件相反,调用becomeActiveLocked(),做退出Doze模式Idle状态的工作。这两个方法在下面的内容中进行分析。

分析到这里,DIC的启动流程就完毕了。启动流程图及主要工作如下所示:
这里写图片描述

下篇文章中将分析Light Doze模式的实现流程。


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

相关文章

Android 6.0的省电技术Doze作用影响以及避免方法

从android 6.0开始&#xff0c;谷歌引入了两项新的省电技术延长电池使用时间&#xff0c;分别是Doze&#xff08;休眠&#xff09;和App Standby&#xff08;app待命模式&#xff09;&#xff0c;只要app是运行在6.0&#xff08;api 23&#xff09;及以上的系统&#xff0c;无论…

Doze模式简介

Doze模式是自Android 6.0开始引入的两项省电功能的其中之一&#xff0c;还有一个就是appstandby&#xff0c;通过管理应用在设备未连接至电源时的行为方式&#xff0c;帮助用户延长电池寿命。当用户长时间未使用设备时&#xff0c;低电耗模式会延迟应用的后台 CPU 和网络活动&a…

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;..$$$$$$$$$$$$$.. ..$...........$.. $$$.$$$$$$$$$.$$$ $...$.......$...$ $.$$$.$$$$$.$$$.$ $.$...$...$...$.$ $.$.$$$.$.$$…