Android SystemUI组件(09)唤醒亮屏 锁屏处理流程

该系列文章总纲链接:专题分纲目录 Android SystemUI组件


本章关键点总结 & 说明:

说明:本章节持续迭代之前章节的思维导图,主要关注左侧上方锁屏分析部分 唤醒亮屏  即可。

Power按键的处理逻辑最终是由PhoneWindowManager来完成。想了解更多可参考输入子系统的相关文章。整理如下:

Android Framework 输入子系统(01)核心机制 inotify和epoll

Android Framework 输入子系统(02)核心机制 双向通信(socketpair+binder)

Android Framework 输入子系统(03)输入系统框架

Android Framework 输入子系统(04)InputReader解读

Android Framework 输入子系统(05)InputDispatcher解读

Android Framework 输入子系统(06)Global Key 一键启动 应用程序案例

Android Framework 输入子系统(07)APP建立联系

Android Framework 输入子系统(08)View基础(activity window decor view)

Android Framework 输入子系统(09)InputStage解读

Android Framework 输入子系统(10)Input命令解读

Android Framework 输入子系统(11)sendevent与getevent命令解读

本章我们只关注与Power按键相关的内容,InputManagerService处理的按键事件,最终会传递到PhoneWindowManager的interceptKeyBeforeQueueing方法中处理。我们就从这里入手逐步分析。

1 从PhoneWindowManager到PowerManagerService的处理

从PhoneWindowManager的interceptKeyBeforeQueueing方法入口开始分析,代码实现如下:

public class PhoneWindowManager implements WindowManagerPolicy {static final String TAG = "WindowManager";static final boolean DEBUG = false;//...public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {//...//表示屏幕是否点亮final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;final boolean canceled = event.isCanceled();//获取按键编码final int keyCode = event.getKeyCode();final boolean keyguardActive = (mKeyguardDelegate == null ? false :(interactive ?isKeyguardShowingAndNotOccluded() :mKeyguardDelegate.isShowing()));boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0|| event.isWakeKey();//...// Handle special keys.switch (keyCode) {//...case KeyEvent.KEYCODE_POWER: {result &= ~ACTION_PASS_TO_USER;isWakeKey = false; // wake-up will be handled separatelyif (down) {//亮屏 step1 按下power按键,亮屏流程interceptPowerKeyDown(event, interactive);} else {//抬起power按键interceptPowerKeyUp(event, interactive, canceled);}break;}case KeyEvent.KEYCODE_SLEEP: {result &= ~ACTION_PASS_TO_USER;if (!mPowerManager.isInteractive()) {useHapticFeedback = false; // suppress feedback if already non-interactive}mPowerManager.goToSleep(event.getEventTime(),PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);isWakeKey = false;break;}case KeyEvent.KEYCODE_WAKEUP: {result &= ~ACTION_PASS_TO_USER;isWakeKey = true;break;}//...}//...if (isWakeKey) {wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey);}return result;}private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {//...mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered|| mScreenshotChordVolumeUpKeyTriggered;if (!mPowerKeyHandled) {//交互模式,表示现在亮屏,点击后会走灭屏流程//当然,灭屏sleep的流程更多的处理在interceptPowerKeyUp中if (interactive) {if (hasLongPressOnPowerBehavior()) {Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);msg.setAsynchronous(true);mHandler.sendMessageDelayed(msg,ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());}} else {//亮屏 step2:非交互模式,表示现在灭屏,点击后会走亮屏流程wakeUpFromPowerKey(event.getDownTime());final int maxCount = getMaxMultiPressPowerCount();if (maxCount <= 1) {mPowerKeyHandled = true;} else {mBeganFromNonInteractive = true;}}}}private void wakeUpFromPowerKey(long eventTime) {//亮屏 step3 调用wakeUp方法wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey);}private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode) {if (!wakeInTheaterMode && isTheaterModeEnabled()) {return false;}//亮屏 step4 走到PowerManagerService中的WakeUp方法中mPowerManager.wakeUp(wakeTime);return true;}
}

2 从PowerManagerService到Notifier的处理

从PhoneWindowManager的interceptKeyBeforeQueueing方法入口开始分析,最终会到达PowerManagerService的wakeUp方法(从PowerManager到PowerManagerService的调用就不在分析了,这个属于binder通信的范畴),对应的代码实现如下:

public final class PowerManagerService extends SystemServiceimplements Watchdog.Monitor {private static final String TAG = "PowerManagerService";private static final boolean DEBUG = false;//...private final class BinderService extends IPowerManager.Stub {//...@Override // Binder callpublic void wakeUp(long eventTime) {if (eventTime > SystemClock.uptimeMillis()) {throw new IllegalArgumentException("event time must not be in the future");}mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);final int uid = Binder.getCallingUid();final long ident = Binder.clearCallingIdentity();try {wakeUpInternal(eventTime, uid);} finally {Binder.restoreCallingIdentity(ident);}}//...}//...private void wakeUpInternal(long eventTime, int uid) {synchronized (mLock) {if (wakeUpNoUpdateLocked(eventTime, uid)) {updatePowerStateLocked();}}}//...private boolean wakeUpNoUpdateLocked(long eventTime, int uid) {if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE|| !mBootCompleted || !mSystemReady) {return false;}Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");try {switch (mWakefulness) {case WAKEFULNESS_ASLEEP:Slog.i(TAG, "Waking up from sleep (uid " + uid +")...");break;case WAKEFULNESS_DREAMING:Slog.i(TAG, "Waking up from dream (uid " + uid +")...");break;case WAKEFULNESS_DOZING:Slog.i(TAG, "Waking up from dozing (uid " + uid +")...");break;}mLastWakeTime = eventTime;setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);userActivityNoUpdateLocked(eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return true;}//...private void setWakefulnessLocked(int wakefulness, int reason) {if (mWakefulness != wakefulness) {finishWakefulnessChangeLocked();mWakefulness = wakefulness;mWakefulnessChanging = true;mDirty |= DIRTY_WAKEFULNESS;mNotifier.onWakefulnessChangeStarted(wakefulness, reason);}}//...
}

3 Notifier的处理

Notifier的处理包含2个层面,一个是发送SCREEN_ON的广播,通知其他子系统亮屏的消息。一个是通过PhoneWindowManager的处理来逐层执行对应回调onScreenTurnedOn方法。

3.1 从Notifier最终发送SCREEN_ON广播

针对发送广播的逻辑处理流程,代码逻辑流程如下:

final class Notifier {private static final String TAG = "PowerManagerNotifier";private static final boolean DEBUG = false;//...public void onWakefulnessChangeStarted(int wakefulness, int reason) {final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);if (interactive) {handleWakefulnessChange(wakefulness, interactive, reason);} else {mLastReason = reason;}// Start input as soon as we start waking up or going to sleep.mInputManagerInternal.setInteractive(interactive);}//...private void handleWakefulnessChange(final int wakefulness, boolean interactive,final int reason) {//...// Handle changes in the overall interactive state.boolean interactiveChanged = false;synchronized (mLock) {// Broadcast interactive state changes.if (interactive) {// Waking up...interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);if (interactiveChanged) {mActualInteractiveState = INTERACTIVE_STATE_AWAKE;mPendingWakeUpBroadcast = true;mHandler.post(new Runnable() {@Overridepublic void run() {EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);mPolicy.wakingUp();}});updatePendingBroadcastLocked();}} else {//...}}//...}//...private void updatePendingBroadcastLocked() {if (!mBroadcastInProgress&& mActualInteractiveState != INTERACTIVE_STATE_UNKNOWN&& (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast|| mActualInteractiveState != mBroadcastedInteractiveState)) {mBroadcastInProgress = true;mSuspendBlocker.acquire();Message msg = mHandler.obtainMessage(MSG_BROADCAST);msg.setAsynchronous(true);mHandler.sendMessage(msg);}}//...private final class NotifierHandler extends Handler {public NotifierHandler(Looper looper) {super(looper, null, true /*async*/);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_BROADCAST:sendNextBroadcast();break;//...}}}//当设备从睡眠状态唤醒时,会发送 ACTION_SCREEN_ON 广播;当设备准备进入睡眠状态时,会发送 ACTION_SCREEN_OFF 广播。private void sendNextBroadcast() {final int powerState;synchronized (mLock) {//mBroadcastedInteractiveState相关处理//...mBroadcastStartTime = SystemClock.uptimeMillis();powerState = mBroadcastedInteractiveState;}EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);// 根据电源状态发送相应的广播if (powerState == INTERACTIVE_STATE_AWAKE) {sendWakeUpBroadcast(); //发送唤醒广播} else {sendGoToSleepBroadcast(); //发送睡眠广播}}//...private void sendWakeUpBroadcast() {if (ActivityManagerNative.isSystemReady()) {// 发送广播关键API,参数解读如下:// mScreenOnIntent 是一个 Intent,包含了唤醒屏幕的信息// UserHandle.ALL 表示这个广播会发送给所有用户// mWakeUpBroadcastDone 是一个 BroadcastReceiver,用于在广播完成后接收回调// mHandler 是一个 Handler,用于处理广播完成后的回调// 0 是一个 flags,表示广播的权限// 最后的 null, null 是额外的参数,这里没有使用mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,mWakeUpBroadcastDone, mHandler, 0, null, null);} else {EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);sendNextBroadcast();}}//...
}

3.2 PhoneWindowManager.wakingUp方法逐层处理亮屏回调方法

这里主要是从到PhoneWindowManager的wakingUp(policy.wakingUp)方法,再到各层的onScreenTurnedOn方法的处理逻辑流程,从onWakefulnessChangeStarted处开始,代码实现如下:

//Notifier处理
final class Notifier {private static final String TAG = "PowerManagerNotifier";private static final boolean DEBUG = false;//...public void onWakefulnessChangeStarted(int wakefulness, int reason) {final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);if (interactive) {handleWakefulnessChange(wakefulness, interactive, reason);} else {mLastReason = reason;}// Start input as soon as we start waking up or going to sleep.mInputManagerInternal.setInteractive(interactive);}//...private void handleWakefulnessChange(final int wakefulness, boolean interactive,final int reason) {//...// Handle changes in the overall interactive state.boolean interactiveChanged = false;synchronized (mLock) {// Broadcast interactive state changes.if (interactive) {// Waking up...interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);if (interactiveChanged) {mActualInteractiveState = INTERACTIVE_STATE_AWAKE;mPendingWakeUpBroadcast = true;mHandler.post(new Runnable() {@Overridepublic void run() {EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);mPolicy.wakingUp();//这里是处理的关键}});updatePendingBroadcastLocked();}} else {//...}}//...}//...
}

这里会调用到PhoneWindowManager的wakingUp方法。代码实现如下:

//phonewindowmanager@Overridepublic void wakingUp() {synchronized (mLock) {mAwake = true;mKeyguardDrawComplete = false;if (mKeyguardDelegate != null) {mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);}updateWakeGestureListenerLp();updateOrientationListenerLp();updateLockScreenTimeout();}if (mKeyguardDelegate != null) {mKeyguardDelegate.onScreenTurnedOn(mKeyguardDelegateCallback);// ... eventually calls finishKeyguardDrawn} else {finishKeyguardDrawn();}}

这里会调用到KeyguardDelegate的onScreenTurnedOn方法。代码实现如下:

//KeyguardDelegatepublic void onScreenTurnedOn(final ShowListener showListener) {if (mKeyguardService != null) {mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(showListener));} else {// try again when we establish a connectionSlog.w(TAG, "onScreenTurnedOn(): no keyguard service!");// This shouldn't happen, but if it does, show the scrim immediately and// invoke the listener's callback after the service actually connects.mShowListenerWhenConnect = showListener;showScrim();}mKeyguardState.screenIsOn = true;}

这里会调用到KeyguardServiceWrapper的onScreenTurnedOn方法。代码实现如下:

//wrapper@Override // Binder interfacepublic void onScreenTurnedOn(IKeyguardShowCallback result) {try {mService.onScreenTurnedOn(result);} catch (RemoteException e) {Slog.w(TAG , "Remote Exception", e);}}

这里会调用到KeyguardService的onScreenTurnedOn方法。代码实现如下:

//KeyguardService//binder@Override // Binder interfacepublic void onScreenTurnedOn(IKeyguardShowCallback callback) {checkPermission();mKeyguardViewMediator.onScreenTurnedOn(callback);}

这里会调用到KeyguardViewMediator的onScreenTurnedOn方法。这里才是真正的逻辑实现,代码实现如下:

//KeyguardViewMediator
public void onScreenTurnedOn(IKeyguardShowCallback callback) {synchronized (this) {// 将屏幕状态设置为开启mScreenOn = true;// 取消任何计划的延迟显示锁屏任务cancelDoKeyguardLaterLocked();// 如果提供了回调,通知屏幕已点亮if (callback != null) {notifyScreenOnLocked(callback);}}// 通知KeyguardUpdateMonitor屏幕已被点亮KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurnedOn();// 可能发送一个用户存在的广播,表示用户已经与设备交互maybeSendUserPresentBroadcast();
}

这里总结下,onScreenTurnedOn关键逻辑解读如下:

  1. 当屏幕再次打开时,这个方法会被调用。
  2. 首先会调用 cancelDoKeyguardLaterLocked 方法,这个方法会取消任何之前计划的延迟显示锁屏任务。
  3. 如果提供了回调接口,会通过 notifyScreenOnLocked 方法通知系统屏幕已经打开。
  4. KeyguardUpdateMonitor 会分派屏幕已打开的事件。
  5. maybeSendUserPresentBroadcast 方法可能会发送一个用户存在的广播,这通常意味着设备已解锁。

3.3 cancelDoKeyguardLaterLocked的处理

 cancelDoKeyguardLaterLocked的代码实现如下:

//KeyguardViewMediatorprivate void cancelDoKeyguardLaterLocked() {mDelayedShowingSequence++;}

注意,在灭屏处理的时候有可能会调用到doKeyguardLaterLocked方法,代码实现如下:

//KeyguardViewMediator// 在锁定屏幕之前给予一定延迟的方法private void doKeyguardLaterLocked() {final ContentResolver cr = mContext.getContentResolver();// 读取显示设置中的屏幕关闭超时时间long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);// 读取安全设置中的锁屏超时时间final long lockAfterTimeout = Settings.Secure.getInt(cr,Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);// 从设备策略管理器获取设备策略超时时间final long policyTimeout = mLockPatternUtils.getDevicePolicyManager().getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser());long timeout;if (policyTimeout > 0) {// 如果设备策略超时时间有效,则取屏幕关闭超时时间和设备策略超时时间的较小值// 但不超过锁屏超时时间displayTimeout = Math.max(displayTimeout, 0); // 忽略负值timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);} else {// 如果没有设备策略限制,则使用锁屏超时时间timeout = lockAfterTimeout;}if (timeout <= 0) {// 如果计算出的延迟时间小于等于0,立即锁定屏幕mSuppressNextLockSound = true; // 抑制锁屏声音doKeyguardLocked(null); // 立即执行锁屏} else {// 如果延迟时间大于0,则设置一个闹钟,在将来的某个时间点锁定屏幕long when = SystemClock.elapsedRealtime() + timeout; // 计算闹钟触发的时间Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);intent.putExtra("seq", mDelayedShowingSequence);// 创建一个PendingIntent,当闹钟触发时,系统将通过这个PendingIntent发送一个广播PendingIntent sender = PendingIntent.getBroadcast(mContext,0, intent, PendingIntent.FLAG_CANCEL_CURRENT);mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender); // 设置闹钟}}

在这段代码逻辑的最后,会创建一个PendingIntent,当闹钟触发时,系统将通过这个PendingIntent发送一个广播DELAYED_KEYGUARD_ACTION。然后通过一个广播接收器来处理该广播消息,处理相关代码如下:

//KeyguardViewMediator//systemReady后开始执行private void setup() {//...注册接收DELAYED_KEYGUARD_ACTION广播消息的ReceivermContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));//...}// 定义一个广播接收器,用于接收特定动作的广播private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// 检查接收到的Intent是否是延迟锁屏的动作if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {// 从Intent中获取序列号final int sequence = intent.getIntExtra("seq", 0);synchronized (KeyguardViewMediator.this) {// 检查接收到的序列号是否与当前的延迟显示序列号相匹配if (mDelayedShowingSequence == sequence) {// 如果序列号匹配,表示该广播是有效的,需要显示锁屏// 标记不播放锁屏音效,因为这是自动锁屏,不是由用户直接触发的mSuppressNextLockSound = true;// 调用doKeyguardLocked方法显示锁屏doKeyguardLocked(null);}}}}};//...

在Android系统中,这里的BroadcastReceiver是异步的,这意味着它可能会在任何时候接收到广播消息,而且接收顺序并不是完全可以预测的。有可能在屏幕关闭后,系统发送了延迟显示锁屏的广播,而这个广播在屏幕亮起并且onScreenTurnedOn方法被调用之后才被接收到。这里是可能发生的情况解读如下:

  1. 屏幕关闭onScreenTurnedOff被调用,可能会通过doKeyguardLaterLocked方法发送一个延迟显示锁屏的广播。

  2. 延迟广播发送:系统安排了一个延迟任务,将在一定时间后发送DELAYED_KEYGUARD_ACTION的广播。

  3. 屏幕亮起:用户按下电源键或其他方式唤醒屏幕,onScreenTurnedOn被调用。

  4. 取消延迟任务onScreenTurnedOn中调用cancelDoKeyguardLaterLocked,这通常会增加mDelayedShowingSequence的值,使得之前的延迟显示锁屏任务失效。

  5. 广播接收:如果在onScreenTurnedOn调用cancelDoKeyguardLaterLocked之后,但锁屏显示之前,之前发送的DELAYED_KEYGUARD_ACTION广播被接收,那么BroadcastReceiver中的代码会检查序列号。

  6. 序列号检查:如果mDelayedShowingSequence的值没有变化,或者变化没有被正确地传递到BroadcastReceiver,那么if (mDelayedShowingSequence == sequence)的条件仍然成立,doKeyguardLocked(null)会被调用,锁屏界面会显示。

  7. 锁屏显示:如果序列号匹配,即使onScreenTurnedOn已经调用,锁屏界面仍然会显示。

这个流程说明,尽管onScreenTurnedOn方法被调用且可能取消了延迟显示锁屏的任务,但如果广播在取消之后才被接收,锁屏界面仍有可能显示。这就是为什么在实际设备上,您可能会遇到即使屏幕很快被点亮,锁屏界面仍然会显示的情况。

3.4 关于亮屏后锁屏处理的总结

如果在屏幕关闭时没有立即锁屏,然后在屏幕打开时执行 onScreenTurnedOn:

  • mScreenOn 会被设置为 true,表示屏幕现在是开启状态。
  • cancelDoKeyguardLaterLocked 会被调用,取消任何延迟显示锁屏的任务。
  • 如果没有其他条件阻止锁屏显示(例如,用户正在解锁设备或设备已解锁),则锁屏界面通常会显示。

在 onScreenTurnedOn 方法中,没有直接调用 doKeyguardLocked方法来显示锁屏。但是,如果系统配置要求在屏幕打开时显示锁屏(例如,设备是安全的且用户没有在解锁过程中),则锁屏界面可能会通过其他机制显示。

总结来说,如果在屏幕关闭时没有锁屏,然后在屏幕打开时执行 onScreenTurnedOn 方法,并且没有其他条件阻止锁屏显示,那么锁屏界面应该会显示。这是为了保证设备的安全性,特别是在设备已经配置了安全措施(如PIN、密码、图案)的情况下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/54456.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BUSHOUND的抓包使用详解

BUSHOUND是个过滤软件&#xff0c;确切来说是在windows操作系统它的驱动层USB传输的数据。所以这个数据上可能是与USB的总线上的数据是有一点差异的。 先要选择设备的抓包。所以就是在device这个界面底下&#xff0c;我们首先要选择我们要抓的设备。 尝试下键盘设备 电脑键盘…

【Linux】用虚拟机配置Ubuntu 24.04.1 LTS环境

目录 1.虚拟机安装Ubuntu系统 2.Ubuntu系统的网络配置 3.特别声明 首先我们先要下载VMware软件&#xff0c;大家自己去下啊&#xff01; 1.虚拟机安装Ubuntu系统 我们进去之后点击创建新的虚拟机&#xff0c;然后选择自定义 接着点下一步 再点下一步 进入这个界面之后&…

C语言:预编译过程的剖析

目录 一.预定义符号和#define定义常量 二.#define定义宏 三.宏和函数的对比 四、#和##运算符 五、条件编译 在之前&#xff0c;我们已经介绍了.c文件在运行的过程图解&#xff0c;大的方面要经过两个方面。 一、翻译环境 1.预处理&#xff08;预编译&#xff09; 2.编译 3…

【网络安全】Cookie与ID未强绑定导致账户接管

未经许可,不得转载。 文章目录 前言正文前言 DigiLocker 是一项在线服务,旨在为公民提供一个安全的数字平台,用于存储和访问重要的文档,如 Aadhaar 卡、PAN 卡和成绩单等。DigiLocker 通过多因素身份验证(MFA)来保护用户账户安全,通常包括 6 位数的安全 PIN 和一次性密…

51c视觉~CV~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/11668984 一、 CV确定对象的方向 介绍如何使用OpenCV确定对象的方向(即旋转角度&#xff0c;以度为单位)。 先决条件 安装Python3.7或者更高版本。可以参考下文链接&#xff1a; https://automaticaddison.com/how-to-s…

Redis --- 第二讲 --- 特性和安装

一、背景知识 Redis特性&#xff1a; Redis是一个在内存中存储数据的中间件&#xff0c;用于作为数据库&#xff0c;作为缓存&#xff0c;在分布式系统中能够大展拳脚。Redis的一些特性造就了现在的Redis。 在内存中存储数据&#xff0c;通过一系列的数据结构。MySQL主要是通…

docker学习笔记(1.0)

docker命令 下载镜像相关命令 检索&#xff1a;docker search 比如&#xff1a;docker search nginx 是查看有没有nginx镜像 后面的OK表示是不是官方镜像&#xff0c;如果有就是官方镜像&#xff0c;如果没有就是第三方的。 下载&#xff1a;docker pull 比如&#xff1a…

Redis篇(Redis原理 - 数据结构)(持续更新迭代)

目录 一、动态字符串 二、intset 三、Dict 1. 简介 2. Dict的扩容 3. Dict的rehash 4. 知识小结 四、ZipList 1. 简介 2. ZipListEntry 3. Encoding编码 五、ZipList的连锁更新问题 六、QuickList 七、SkipList 八、RedisObject 1. 什么是 redisObject 2. Redi…

【优选算法】(第八篇)

目录 串联所有单词的⼦串&#xff08;hard&#xff09; 题目解析 讲解算法原理 编写代码 最⼩覆盖⼦串&#xff08;hard&#xff09; 题目解析 讲解算法原理 编写代码 串联所有单词的⼦串&#xff08;hard&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#…

Redis:list类型

Redis&#xff1a;list类型 list命令非阻塞LPUSHLRANGELPUSHXRPUSHRPUSHXLPOPRPOPLINDEXLINSERTLLENLREMLTRIMLSET 阻塞BLPOPBRPOP 内部编码ziplistlinkedlistquicklist 几乎每种语言都有顺序表、数组、链表这样的顺序结构&#xff0c;Redis也做出了相应的支持。 如图&#xff…

Stable Diffusion绘画 | AI 图片智能扩充,超越PS扩图的AI扩图功能(附安装包)

来到「文生图」页面&#xff0c;输入固定的起手式提示词。 第1步&#xff0c;开启 ControlNet&#xff0c;将需要扩充的图片加载进来&#xff1a; 控制类型选择「Inpaint」&#xff0c;预处理器选择「inpaint_onlylama」&#xff0c;缩放模式选择「缩放后填充空白」&#xff1…

基于SpringBoot+Vue的网约车管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

(Django)初步使用

前言 Django 是一个功能强大、架构良好、安全可靠的 Python Web 框架&#xff0c;适用于各种规模的项目开发。它的高效开发、数据库支持、安全性、良好的架构设计以及活跃的社区和丰富的文档&#xff0c;使得它成为众多开发者的首选框架。 目录 安装 应用场景 良好的架构设计…

Leetcode—763. 划分字母区间【中等】

2024每日刷题&#xff08;175&#xff09; Leetcode—763. 划分字母区间 C实现代码 class Solution { public:vector<int> partitionLabels(string s) {int rightmost[26];int l 0;int r 0;for(int i 0; i < s.length(); i) {rightmost[s[i] - a] i;}vector<…

[C语言]第十一节 函数递归一基础知识到高级技巧的全景探索

目录 11.1. 递归是什么&#xff1f; 11.1.1 递归的思想&#xff1a; 11.2 递归的限制条件 举例1&#xff1a;求n的阶乘 画图推演 举例2&#xff1a;顺序打印⼀个整数的每⼀位 画图推演 11.3. 递归与迭代 举例3&#xff1a;求第n个斐波那契数 11.1. 递归是什么&#xff…

一款基于 Java 的可视化 HTTP API 接口快速开发框架,干掉 CRUD,效率爆炸(带私活源码)

平常我们经常需要编写 API&#xff0c;但其实常常只是一些简单的增删改查&#xff0c;写这些代码非常枯燥无趣。 今天给大家带来的是一款基于 Java 的可视化 HTTP API 接口快速开发框架&#xff0c;通过 UI 界面编写接口&#xff0c;无需定义 Controller、Service、Dao 等 Jav…

【Java数据结构】栈 (Stack)

【本节目标】 1. 栈的概念及使用 2. 相关 OJ 题 一、概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last…

【2024】前端学习笔记11-网页布局-弹性布局flex

学习笔记 网页布局弹性布局&#xff1a;flex案例&#xff1a;flex布局案例 网页布局 在页面布局中&#xff0c;display属性用于设置一个元素的显示方式。它可以指定元素是作为块级元素、内联元素还是充当表格元素显示。 display的常见属性值&#xff1a; block&#xff1a;将…

k8s 中微服务之 MetailLB 搭配 ingress-nginx 实现七层负载

目录 1 MetailLB 搭建 1.1 MetalLB 的作用和原理 1.2 MetalLB功能 1.3 部署 MetalLB 1.3.1 创建deployment控制器和创建一个服务 1.3.2 下载MealLB清单文件 1.3.3 使用 docker 对镜像进行拉取 1.3.4 将镜像上传至私人仓库 1.3.5 将官方仓库地址修改为本地私人地址 1.3.6 运行清…

ensp回顾--聚合链路技术简介与详细案例(构建基于交换机到交换机的聚合链路)

文章目录 什么是聚合链路&#xff1f;聚合链路的工作原理聚合链路的优势使用场景 案例ensp版本图例pc的ip地址具体步骤连通性测试 在现代网络中&#xff0c;聚合链路&#xff08;Link Aggregation&#xff09;是一种常见的技术&#xff0c;用于提高网络连接的带宽和可靠性。本文…