该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统


本章关键点总结 & 说明:

本章节主要关注➕ updatePowerStateLocked 方法中 循环处理 和 更新显示设备状态 部分 即可。该章节 主要是 对循环处理部分  和 更新显示设备状态的流程 进行 详细解读。


PMS核心方法updatePowerStateLocked

updatePowerStateLocked 方法是整个PMS中的核心方法,它用来更新整个电源状态的改变,并进行重新计算。PMS中使用一个int值mDirty作为标志位判断电源状态是否发生变化,当电源状态发生改变时,如亮灭屏、电池状态改变、暗屏…都会调用该方法,它的代码实现如下:

private void updatePowerStateLocked() {//...Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");try {// 1 更新基本状态// 1.1 更新mIsPowered,mPlugType,mBatteryLevelupdateIsPoweredLocked(mDirty);// 1.2 更新mStayonupdateStayOnLocked(mDirty);updateScreenBrightnessBoostLocked(mDirty);// 2 更新wakefulness和用户活动final long now = SystemClock.uptimeMillis();int dirtyPhase2 = 0;for (;;) {int dirtyPhase1 = mDirty;dirtyPhase2 |= dirtyPhase1;mDirty = 0;// 2.1 得到当前终端整体的信息,保存到mWakeLockSummary变量中updateWakeLockSummaryLocked(dirtyPhase1);// 2.2 根据用户最后的活动来决定当前屏幕的状态updateUserActivitySummaryLocked(now, dirtyPhase1);// 2.3 判定 第二阶段的电源状态更新是否结束if (!updateWakefulnessLocked(dirtyPhase1)) {break;}}// 3 更新显示设备状态boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);// 4 更新屏保状态updateDreamLocked(dirtyPhase2, displayBecameReady);// 5 发送通知 并 更新底层wakelock// 5.1 发送通知if (mDisplayReady) {finishWakefulnessChangeLocked();}// 5.2 更新底层wakelockupdateSuspendBlockerLocked();} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}
}

该部分代码共分为5部分,因为篇幅较长,所以拆分成3个章节进行分析。本章节是 2 3部分。


2 循环处理

接下来进入到 一个无限的for循环,但循环最多两次就结束。这里关注循环中的三个关键方法:

  1. updateWakeLockSummaryLocked 得到当前终端整体的信息并保存到mWakeLockSummary变量中
  2. updateUserActivitySummaryLocked 根据用户最后的活动来决定当前屏幕的状态
  3. updateWakefulnessLocked 判定 第二阶段的电源状态更新是否结束

2.1 updateWakeLockSummaryLocked

该方法 根据PMS当前持有的所有WakeLock,得到当前终端整体的信息,保存到mWakeLockSummary变量中,代码如下:

private void updateWakeLockSummaryLocked(int dirty) {//PMS持有的WakeLock发生变化,或者唤醒状态发生变化时,才重新进行更新mWakeLockSummary//如果 调用PMS的acquireWakeLock,就会将dirty的DIRTY_WAKE_LOCKS 置为 1if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {mWakeLockSummary = 0;final int numWakeLocks = mWakeLocks.size();for (int i = 0; i < numWakeLocks; i++) {final WakeLock wakeLock = mWakeLocks.get(i);//实现每个level WakeLock对应的注释信息switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {case PowerManager.PARTIAL_WAKE_LOCK:mWakeLockSummary |= WAKE_LOCK_CPU;break;case PowerManager.FULL_WAKE_LOCK:mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;break;case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT;break;case PowerManager.SCREEN_DIM_WAKE_LOCK:mWakeLockSummary |= WAKE_LOCK_SCREEN_DIM;break;case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:mWakeLockSummary |= WAKE_LOCK_PROXIMITY_SCREEN_OFF;break;case PowerManager.DOZE_WAKE_LOCK:mWakeLockSummary |= WAKE_LOCK_DOZE;break;}}//PMS中的mWakefulness变量记录了终端当前的状态//移除在特定状态下,没有意义的WakeLockif (mWakefulness != WAKEFULNESS_DOZING) {//如果不是Dozing状态,移除相应的wakeLock标志位mWakeLockSummary &= ~WAKE_LOCK_DOZE;}if (mWakefulness == WAKEFULNESS_ASLEEP|| (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {//如果当前为Asleep或者有Doze的wakeLock锁的时候,应该移除掉屏幕亮度相关的wakeLock锁mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM| WAKE_LOCK_BUTTON_BRIGHT);if (mWakefulness == WAKEFULNESS_ASLEEP) {//休眠时 sensor不再需要监听终端是否靠近物体,以触发亮灭屏mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;}}//根据当前的状态,及PMS持有的WakeLock,推断出隐含的持锁需求//当PMS持有亮屏锁WAKE_LOCK_SCREEN_BRIGHT时,若当前终端为唤醒态,CPU显然也需要处于唤醒态if ((mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {if (mWakefulness == WAKEFULNESS_AWAKE) {mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;} else if (mWakefulness == WAKEFULNESS_DREAMING) {mWakeLockSummary |= WAKE_LOCK_CPU;}}}
}

特殊说明,Android定义一个mWakeLockSummary变量而不是多个原因:

PMS将WakeLock定义为不同进程的请求信息,这些请求信息对CPU、屏幕和键盘有不同的需求。 对于每一种资源而言,只要有一个申请满足获取条件,PMS就需要为终端分配该申请对应的资源。 例如:假设PMS有20个WakeLock,只有1个申请亮屏,另外19个只申请CPU唤醒,PMS仍然需要保持终端亮屏。 因此,mWakeLockSummary 就提供了一种整合多个WakeLock请求的功能,方便PMS进行集中控制。

2.2 updateUserActivitySummaryLocked

updateUserActivitySummaryLocked主要根据用户最后的活动来决定当前屏幕的状态。代码如下:

private void updateUserActivitySummaryLocked(long now, int dirty) {// Update the status of the user activity timeout timer.if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY| DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);long nextTimeout = 0;if (mWakefulness == WAKEFULNESS_AWAKE|| mWakefulness == WAKEFULNESS_DREAMING|| mWakefulness == WAKEFULNESS_DOZING) {//获取进入休眠状态的时间sleepTimeout/*getSleepTimeoutLocked中会判断休眠时间和屏幕熄灭时间的关系如果休眠时间sleepTimeout小于屏幕熄灭时间screenOfftime则休眠时间被调整为屏幕熄灭时间,因为屏幕亮屏状态下,终端不能进入休眠*/final int sleepTimeout = getSleepTimeoutLocked();//获取屏幕熄灭的时间final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);//获取屏幕变暗的时间final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);mUserActivitySummary = 0;//在唤醒的状态下,发生过用户事件if (mLastUserActivityTime >= mLastWakeTime) {//重新计算出屏幕需要变暗的时间nextTimeout = mLastUserActivityTime+ screenOffTimeout - screenDimDuration;if (now < nextTimeout) {//如果没有到达需要变暗的时间,那么当前屏幕的状态为USER_ACTIVITY_SCREEN_BRIGHT(亮屏)mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;} else {//到达变暗的时间,则计算出屏幕熄灭的时间nextTimeout = mLastUserActivityTime + screenOffTimeout;if (now < nextTimeout) {//还没到熄灭的时间,则当前屏幕的状态为USER_ACTIVITY_SCREEN_DIM(暗屏)mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;}}}if (mUserActivitySummary == 0//mLastUserActivityTimeNoChangeLights表示用户最后的活动不会改变屏幕当前的状态&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {//计算下次屏幕熄灭的时间nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;//还未到达熄屏时间if (now < nextTimeout) {if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) {//当前屏幕是亮屏,仍然设置为亮屏mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;} else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {//当前屏幕是变暗,仍然设置为变暗mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;}}}if (mUserActivitySummary == 0) {//若定义了有效的休眠时间if (sleepTimeout >= 0) {//计算用户最后的活动时间final long anyUserActivity = Math.max(mLastUserActivityTime,mLastUserActivityTimeNoChangeLights);//只有在唤醒状态下,进行了用户活动,才会重新更新休眠时间if (anyUserActivity >= mLastWakeTime) {nextTimeout = anyUserActivity + sleepTimeout;if (now < nextTimeout) {//屏幕已经熄灭,但还未到达休眠状态,先进入dream态mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;}}} else {//直接进入dream态mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;nextTimeout = -1;}}/*根据nextTimeOut延迟发送信息,处理后重新调用updatePowerStateLocked再次进入到该方法,不断循环并评估 是否根据用户动作亮、熄屏等*/if (mUserActivitySummary != 0 && nextTimeout >= 0) {Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextTimeout);}} else {mUserActivitySummary = 0;}}
}

这里主要用mUserActivitySummary变量存储当前屏幕的状态。 一共有3中基本状态:

  1. USER_ACTIVITY_SCREEN_BRIGHT 点亮屏幕
  2. USER_ACTIVITY_SCREEN_DIM 屏幕变暗
  3. USER_ACTIVITY_SCREEN_DREAM 屏保状态

从代码可以看出,屏幕变化和userActivity活动有关,它根据最后的userActivity活动时间决定点亮屏幕、调暗屏幕或熄灭屏幕。

为什么用户一直操作手机,屏幕不会熄灭或者变暗?

很多方法中都会调用userActivityNoUpdateLocked方法。该方法将触发一次用户活动,以更新用户活动的时间,这样屏幕变暗和熄灭时间就会重新进行计算。

2.3 updateWakefulnessLocked

updateWakefulnessLocked将决定第二阶段的电源状态更新是否结束,代码如下:

private boolean updateWakefulnessLocked(int dirty) {boolean changed = false;//只要之前的流程更改过mDirty就会进入分支if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE| DIRTY_DOCK_STATE)) != 0) {//如果当前的状态是唤醒,isItBedTimeYetLocked判定不能再保持唤醒态if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {final long time = SystemClock.uptimeMillis();//主要根据设置信息,判断是否满足进入Dream状态的条件if (shouldNapAtBedTimeLocked()) {//将mWakefullness的值置为WAKEFULNESS_DREAMING//修改mDirty变量,并进行通知//将mSandmanSummoned 置为 truechanged = napNoUpdateLocked(time, Process.SYSTEM_UID);} else {//将mWakefullness的值置为WAKEFULNESS_DOZING//如果系统设置了跳过Dozing态,则将mWakefullness置为WAKEFULNESS_ASLEEP//同时修改mDirty变量,并进行通知//将mSandmanSummoned 置为 truechanged = goToSleepNoUpdateLocked(time,PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);}}}return changed;
}

这里涉及isItBedTimeYetLocked函数,代码如下:

private boolean isItBedTimeYetLocked() {//主要由isBeingKeptAwakeLocked决定return mBootCompleted && !isBeingKeptAwakeLocked();
}

继续深入,isBeingKeptAwakeLocked的代码如下:

//根据状态,判断终端是否应该处于唤醒状态
private boolean isBeingKeptAwakeLocked() {return mStayOn|| mProximityPositive|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT| USER_ACTIVITY_SCREEN_DIM)) != 0|| mScreenBrightnessBoostInProgress;
}

整体来看:

如果终端可以一直保持唤醒状态,或一开始就是非唤醒态, 那么mWakefulness不会发生改变,第二阶段的for循环将会break。

如果终端要从唤醒态变为非唤醒态,那么for循环将再运行一次,重新计算mWakeLockSummary和mUserActivitySummary。(updateWakeLockSummaryLocked和updateUserActivitySummaryLocked函数的一些计算,与终端是否处于唤醒状态,即mWakefulness值 有关)。由于这两个函数并不会修改mWakefulness,因此在这一次运行时,updateWakefulnessLocked将返回false,即第二阶段结束,即:更新电源状态的第二阶段,正常情况下最多运行两次。


3 更新显示设备状态

3.1 updateDisplayPowerStateLocked 分析

更新了PowerManagerService中表示显示屏状态的变量mDisplayPowerRequest.screenState,以及重新确定屏幕的亮度,最后调用了DisplayPowerController的requestPowerState方法,代码如下:

private boolean updateDisplayPowerStateLocked(int dirty) {final boolean oldDisplayReady = mDisplayReady;//mDirty满足条件时,进入以下分支if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {//根据mWakefullness、mWakeLockSummary、mUserActivitySummary等,决定屏幕的policy//policy定义为DisplayPowerRequest.(POLICY_OFF、POLICY_DOZE、POLICY_BRIGHT和POLICY_DIM)mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();//决定屏幕的亮度int screenBrightness = mScreenBrightnessSettingDefault;//...// 更新mDisplayPowerRequest的参数mDisplayPowerRequest.screenBrightness = screenBrightness;//...//实际上调用DisplayPowerController的requestPowerState函数//在初始时,PMS注册了mDisplayPowerCallbacks到DisplayPowerController中,//当更新完成后,会回调定义的接口,重新updatePowerStateLockedmDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,mRequestWaitForNegativeProximity);mRequestWaitForNegativeProximity = false;}return mDisplayReady && !oldDisplayReady;
}

这里 会调用到DisplayManagerService的LocalService中的requestPowerState,代码如下:

public boolean requestPowerState(DisplayPowerRequest request,boolean waitForNegativeProximity) {return mDisplayPowerController.requestPowerState(request,waitForNegativeProximity);
}

接下来会进入到 DisplayPowerController类中,该类主要是管理Power状态,主要功能是播放屏幕状态变化的动画,通过回调方式和PMS进行交互,这里会调用到 DisplayPowerController的requestPowerState函数的实现,代码如下:

public boolean requestPowerState(DisplayPowerRequest request,boolean waitForNegativeProximity) {synchronized (mLock) {boolean changed = false;//proximity sensor需要检测距离if (waitForNegativeProximity&& !mPendingWaitForNegativeProximityLocked) {mPendingWaitForNegativeProximityLocked = true;changed = true;}//参数中的Request对于DisplayPowerController而言,是一个 新需求if (mPendingRequestLocked == null) {mPendingRequestLocked = new DisplayPowerRequest(request);changed = true;} else if (!mPendingRequestLocked.equals(request)) {mPendingRequestLocked.copyFrom(request);changed = true;}if (changed) {//一但有新的需求,mDisplayReadyLocked就是false,表示屏幕有待调整mDisplayReadyLocked = false;}//有新需求,同时有对应的requestif (changed && !mPendingRequestChangedLocked) {mPendingRequestChangedLocked = true;//发送消息,更新屏幕状态//最终通过DisplayPowerController的updatePowerState函数,进行屏幕状态更新//更新屏幕状态后,将回调PMS的接口sendUpdatePowerStateLocked();}return mDisplayReadyLocked;}
}

当PMS传入新mDisplayPowerRequest时,requestPowerState返回false;当DisplayPowerController按照mDisplayPowerRequest修改完屏幕状态,再次进入回到updateDisplayPowerStateLocked函数,调用requestPowerState时才会返回true。

3.2 sendUpdatePowerStateLocked的流程

同时 这里最后调用了sendUpdatePowerStateLocked方法,该方法 是开始播放各种打开/关闭 屏幕的动画,或者屏幕变暗、变亮的动画,通过发送消息MSG_UPDATE_POWER_STATE,消息通过updatePowerState来处理,之后将mDisplayReadyLocked的值 设置为true,这样屏幕就能关闭了,updatePowerState的代码如下:

private void updatePowerState() {//播放动画...if (ready && mustNotify) {// Send state change.synchronized (mLock) {if (!mPendingRequestChangedLocked) {mDisplayReadyLocked = true;}}sendOnStateChangedWithWakelock();//发送通知}//...
}

这里继续分析 sendOnStateChangedWithWakelock ,代码如下:

private void sendOnStateChangedWithWakelock() {mCallbacks.acquireSuspendBlocker();mHandler.post(mOnStateChangedRunnable);
}

这里post消息处理 代码如下:

private final Runnable mOnStateChangedRunnable = new Runnable() {@Overridepublic void run() {mCallbacks.onStateChanged();mCallbacks.releaseSuspendBlocker();}
};

这里继续分析 处理消息的run()方法中 的mCallbacks.onStateChanged方法,在PMS中 定义了Callback 方法,如下所示:

private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =new DisplayManagerInternal.DisplayPowerCallbacks() {private int mDisplayState = Display.STATE_UNKNOWN;@Overridepublic void onStateChanged() {synchronized (mLock) {mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;updatePowerStateLocked();}}//...
}

这里又调用了updatePowerStateLocked(),重新开始处理Power系统的状态更新。

3.3 总结

在整个阶段,PMS根据之前得到信息,构造出DisplayPowerRequest,然后发送给DisplayPowerController进行实际的处理。 当DisplayPowerController完成实际的工作(部分工作还依赖于PhoneWindowManager)后,再通知PMS进行复查。因此PMS的定位是一个管理者; 负责整个终端信息的搜集和维护,将相应的工作指派给其他模块执行; 其他模块执行完成后向PMS汇报;PMS再检查工作的完成情况,之后 做出下一步指示。

Android Framework 电源子系统(04)核心方法updatePowerStateLocked分析-2 循环处理  更新显示设备状态相关推荐

  1. Android Framework 电源子系统(05)核心方法updatePowerStateLocked分析-3 更新屏保  发送通知  更新wakelock

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ updatePowerStateLocked 方法中 更新屏保 ...

  2. Android Framework 电源子系统(01)PowerManagerService启动分析

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ 以上思维导图即可.该章节 主要是 对 PMS 启动的分析,从sy ...

  3. Android Framework 窗口子系统 (08)窗口动画之动画系统框架

    该系列文章总纲链接:专题分纲目录 Android Framework 窗口子系统 本章关键点总结 & 说明: 导图是不断迭代的,这里主要关注➕ 左上角 Android 窗口动画系统部分(因为导 ...

  4. 专题总纲目录 Android Framework 总纲

    专题总纲说明: 本系列文章虽说是 Android 的知识体系专题,同时也是学习Android Framework 系统的一个思路,尤其是当我们对Android 框架层 一点都不了解的时候,但前提是要有 ...

  5. Android Framework中的线程Thread及它的threadLoop方法

    当初跟踪Camera的代码中的时候一直追到了HAL层,而在Framework中的代码看见了许许多多的Thread.它们普遍的特点就是有一个threadLoop方法.按照字面的意思应该是这个线程能够循环 ...

  6. android系统自动休眠代码流程,Android P 电源管理(4)待机流程

    电视遥控器,短端走待机(str待机)流程, 长按是关机,走关机流程,记录下待机流程. 参考博客 待机流程 Android P引入自动待机功能,只有存在WakeLock,wakeup_count就不会为 ...

  7. kvc实践一:核心方法和进阶

    kvc概念和核心方法 NSObject类中,有一个显示的类别名NSKeyValueCoding(缩写kvc),所以继承自NSObject的类都可以使用kvc,下面是kvc的4个主要方法: - (nul ...

  8. iphone屏幕镜像连电视_三种投屏方法,让电视电脑也能显示iPhone画面,大屏游戏视频更佳...

    使用iPhone的投屏功能可以将手机端的画面投射到电脑.电视.投影仪等设备上,实现大屏共享,看电视玩游戏体验更好. 其实在IOS11前iPhone自带的AirPlay可以实现轻松投屏.只要在同一个网络 ...

  9. 千里马android framework开发解决Accessing hidden method限制,让应用访问隐藏方法(需要可以修改系统源码方案)

    hi,粉丝朋友们大家好! 今天来给大家分享一下,就是经常大家会做安卓系统开发工作问到一个问题,那就是我如果framework代码中增加了一个方法啥的,但是我又不想公开给第三方应用知道,只想让我系统的应 ...

最新文章

  1. DFS(6)——hdu1342Lotto
  2. docker 全部杀掉
  3. 如何深入浅出地讲解麦克斯韦方程组
  4. MSP432P401R学习:CCS入门实验练习,使用CCS新建、导入、编译、下载工程
  5. 中国能源统计年鉴面板数据-分省市主要污染物排放指标(包含ECXEL2020年中国统计年鉴)
  6. html 如何播放 dat音频,如何打开dat音频文件,教您如何打开dat音频文件
  7. 李煜 天涯 青砚1989
  8. 【JAVA】RequestResponse
  9. zmq pub/sub使用详解
  10. 【金融项目】尚融宝项目(九)
  11. linux可用直播软件,免费直播软件OBS Studio下载 支持Windows/Mac和Linux
  12. 黑马头条项目-Vue-day9-文章详情模块、关注与取消关注,点赞和喜欢功能
  13. 计算机怎么让隐藏的文件夹不能搜索,电脑怎么巧妙隐藏文件夹让人找不到?隐藏电脑文件方法教程...
  14. 企业级购物车实现思路
  15. 科技爱好者周刊:第 87 期
  16. 图像处理问题解决师——求取每个颗粒到其他颗粒边缘的最近距离
  17. 高中计算机教师专业,高中计算机教师资格证,要计算机专业证书吗
  18. 关闭Windows系统中的UAC用户帐户控制
  19. 【AI周报】首款高容错通用量子计算机原型登上Nature;SIGIR 2022 | 快手联合武汉大学提出序列推荐的多粒度神经模型
  20. 苹果承认新一代iPad wifi问题 正调查原因

热门文章

  1. [BJWC2018]第k大斜率
  2. 根号3表白html,数字表白公式 表白套路情话
  3. java 金额的大小写转换类
  4. 一篇讲给自己听的k8s网络模型
  5. 班级日常工作管理系统
  6. Android Compose——一个简单的Bilibili APP
  7. 从赤壁之战看刘备与曹操暴露的最大差距
  8. The Game C语言
  9. React: 跳转页面+刷新(登陆成功后的动作)
  10. mysql数据库中吧时分秒换算成秒的函数TIME_TO_SEC()