Android SystemUI组件(08)睡眠灭屏 锁屏处理流程

news2024/11/24 20:52:56

该系列文章总纲链接:专题分纲目录 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的处理

public class PhoneWindowManager implements WindowManagerPolicy {
    static final String TAG = "WindowManager";
    static final boolean DEBUG = false;
    //...
    //关键流程step1
    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 separately
                if (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;
    }
    //...
    //关键流程step2
    private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
        final boolean handled = canceled || mPowerKeyHandled;
        mScreenshotChordPowerKeyTriggered = false;
        cancelPendingScreenshotChordAction();
        cancelPendingPowerKeyAction();

        if (!handled) {
            // Figure out how to handle the key now that it has been released.
            mPowerKeyPressCounter += 1;

            final int maxCount = getMaxMultiPressPowerCount();
            final long eventTime = event.getDownTime();
            if (mPowerKeyPressCounter < maxCount) {
                // This could be a multi-press.  Wait a little bit longer to confirm.
                // Continue holding the wake lock.
                Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
                        interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
                msg.setAsynchronous(true);
                mHandler.sendMessageDelayed(msg, ViewConfiguration.getDoubleTapTimeout());
                return;
            }

            // No other actions.  Handle it immediately.
            powerPress(eventTime, interactive, mPowerKeyPressCounter);
        }

        // Done.  Reset our state.
        finishPowerKeyPress();
    }
    //...
    //关键流程step3
    private void powerPress(long eventTime, boolean interactive, int count) {
        if (mScreenOnEarly && !mScreenOnFully) {
            return;
        }

        if (count == 2) {
            powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
        } else if (count == 3) {
            powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
        } else if (interactive && !mBeganFromNonInteractive) {
            //关键流程step4 除了无效处理意外,都会调用到PowerManagerService中的goToSleep方法
            switch (mShortPressOnPowerBehavior) {
                case SHORT_PRESS_POWER_NOTHING:
                    break;
                case SHORT_PRESS_POWER_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    launchHomeFromHotKey();
                    break;
            }
        }
    }
    //...
}

2 从PowerManagerService到Notifier的处理

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

public final class PowerManagerService extends SystemService
        implements Watchdog.Monitor {
    private static final String TAG = "PowerManagerService";

    private static final boolean DEBUG = false;
    //...
    //关键流程step1
    private final class BinderService extends IPowerManager.Stub {
        @Override // Binder call
        public void goToSleep(long eventTime, int reason, int flags) {
            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 {
                goToSleepInternal(eventTime, reason, flags, uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
        //...
    }
    //...
	//关键流程step2
    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }
    //...
    //关键流程step3
    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
        if (eventTime < mLastWakeTime
                || mWakefulness == WAKEFULNESS_ASLEEP
                || mWakefulness == WAKEFULNESS_DOZING
                || !mBootCompleted || !mSystemReady) {
            return false;
        }

        try {
            //...
            mLastSleepTime = eventTime;
            mSandmanSummoned = true;
            setWakefulnessLocked(WAKEFULNESS_DOZING, reason);

            // Report the number of wake locks that will be cleared by going to sleep.
            int numWakeLocksCleared = 0;
            final int numWakeLocks = mWakeLocks.size();
            for (int i = 0; i < numWakeLocks; i++) {
                final WakeLock wakeLock = mWakeLocks.get(i);
                switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                    case PowerManager.FULL_WAKE_LOCK:
                    case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                    case PowerManager.SCREEN_DIM_WAKE_LOCK:
                        numWakeLocksCleared += 1;
                        break;
                }
            }

            // Skip dozing if requested.
            if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
                reallyGoToSleepNoUpdateLocked(eventTime, uid);
            }
        } 
        return true;
    }
    //...
	//关键流程step4
	private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {
        if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
                || !mBootCompleted || !mSystemReady) {
            return false;
        }

        try {
            mDirty |= DIRTY_WAKEFULNESS;
            mWakefulness = WAKEFULNESS_ASLEEP;
            setInteractiveStateLocked(false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
        }
        return true;
    }

    //关键流程step5
    private void setInteractiveStateLocked(boolean interactive, int reason) {
        if (mInteractive != interactive) {
            finishInteractiveStateChangeLocked();

            mInteractive = interactive;
            mInteractiveChanging = true;
            mNotifier.onInteractiveStateChangeStarted(interactive, reason);
        }
    }

    //...
    //关键流程step6
    private void finishInteractiveStateChangeLocked() {
        if (mInteractiveChanging) {
            mNotifier.onInteractiveStateChangeFinished(mInteractive);
            mInteractiveChanging = false;
        }
    }
    //...
}

3 Notifier的处理

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

3.1 从Notifier最终发送SCREEN_OFF广播

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

//Notifier
final class Notifier {
    private static final String TAG = "PowerManagerNotifier";
    private static final boolean DEBUG = false;
    //...
    //关键流程step1
    public void onInteractiveStateChangeFinished(boolean interactive) {
        synchronized (mLock) {
            if (!interactive) {
                if (mActualPowerState != POWER_STATE_ASLEEP) {
                    mActualPowerState = POWER_STATE_ASLEEP;
                    mPendingGoToSleepBroadcast = true;
                    if (mUserActivityPending) {
                        mUserActivityPending = false;
                        mHandler.removeMessages(MSG_USER_ACTIVITY);
                    }
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
                            switch (mLastGoToSleepReason) {
                                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
                                    break;
                                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
                                    break;
                            }
                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
                            mPolicy.goingToSleep(why);
                            mActivityManagerInternal.goingToSleep();
                        }
                    });
                    updatePendingBroadcastLocked();
                }
            }
        }
	}
    //...
    //关键流程step2
    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);
        }
    }
    //...
    //关键流程step3
     private final class NotifierHandler extends Handler {
        public NotifierHandler(Looper looper) {
            super(looper, null, true /*async*/);
        }

        @Override
        public 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 sendGoToSleepBroadcast() {
        if (ActivityManagerNative.isSystemReady()) {
            // 发送广播关键API,参数解读如下:
            // mScreenOnIntent 是一个 Intent,包含了唤醒屏幕的信息
            // UserHandle.ALL 表示这个广播会发送给所有用户
            // mWakeUpBroadcastDone 是一个 BroadcastReceiver,用于在广播完成后接收回调
            // mHandler 是一个 Handler,用于处理广播完成后的回调
            // 0 是一个 flags,表示广播的权限
            // 最后的 null, null 是额外的参数,这里没有使用
            mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
                    mGoToSleepBroadcastDone, mHandler, 0, null, null);
        } else {
            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
            sendNextBroadcast();
        }
    }
    //...
}

这条逻辑的处理主要是发送睡眠广播为主。接下来分析从mPolicy.goingToSleep(why);出发的灭屏回调处理流程。

3.2 PhoneWindowManager的goingToSleep方法处理灭屏回调方法

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

//Notifier
final class Notifier {
    private static final String TAG = "PowerManagerNotifier";
    private static final boolean DEBUG = false;
    //...
    public void onInteractiveStateChangeFinished(boolean interactive) {
        synchronized (mLock) {
            if (!interactive) {
                if (mActualPowerState != POWER_STATE_ASLEEP) {
                    mActualPowerState = POWER_STATE_ASLEEP;
                    mPendingGoToSleepBroadcast = true;
                    if (mUserActivityPending) {
                        mUserActivityPending = false;
                        mHandler.removeMessages(MSG_USER_ACTIVITY);
                    }
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
                            switch (mLastGoToSleepReason) {
                                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
                                    break;
                                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
                                    break;
                            }
                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
                            mPolicy.goingToSleep(why);
                            mActivityManagerInternal.goingToSleep();
                        }
                    });
                    updatePendingBroadcastLocked();
                }
            }
        }
	}
    //...
}

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

//phonewindowmanager
    @Override
    public void goingToSleep(int why) {
        EventLog.writeEvent(70000, 0);
        synchronized (mLock) {
            mAwake = false;
            mKeyguardDrawComplete = false;
            updateWakeGestureListenerLp();
            updateOrientationListenerLp();
            updateLockScreenTimeout();
        }

        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onScreenTurnedOff(why);
        }
    }

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

//KeyguardDelegate
    public void onScreenTurnedOff(int why) {
        if (mKeyguardService != null) {
            mKeyguardService.onScreenTurnedOff(why);
        }
        mKeyguardState.offReason = why;
        mKeyguardState.screenIsOn = false;
    }

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

//wrapper
    @Override // Binder interface
    public void onScreenTurnedOff(int reason) {
        try {
            mService.onScreenTurnedOff(reason);
        } catch (RemoteException e) {
            Slog.w(TAG , "Remote Exception", e);
        }
    }

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

//KeyguardService
    //binder
        @Override // Binder interface
        public void onScreenTurnedOff(int reason) {
            checkPermission();
            mKeyguardViewMediator.onScreenTurnedOff(reason);
        }

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

//KeyguardViewMediator
    public void onScreenTurnedOff(int why) {
		synchronized (this) {
			// 将屏幕状态设置为关闭
			mScreenOn = false;
			
			// 重置关键的锁屏完成等待状态
			resetKeyguardDonePendingLocked();
			
			// 标记未运行隐藏动画
			mHideAnimationRun = false;
			
			// 判断是否需要立即锁屏
			// 如果设置为按下电源键立即锁屏,或者设备没有设置安全措施,则立即锁屏
			final boolean lockImmediately =
				mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure();
			
			// 通知屏幕已关闭
			notifyScreenOffLocked();

			// 如果存在退出安全模式的回调,执行回调并设置为null
			if (mExitSecureCallback != null) {
				// 执行回调以通知锁屏退出的结果
				try {
					mExitSecureCallback.onKeyguardExitResult(false);
				} catch (RemoteException e) {
					// 如果远程调用失败,记录错误
					Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
				}
				mExitSecureCallback = null;
				
				// 如果锁屏没有被外部启用,隐藏锁屏
				if (!mExternallyEnabled) {
					hideLocked();
				}
			} else if (mShowing) {
				// 如果锁屏当前正在显示,重置锁屏状态
				resetStateLocked();
			} else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT ||
				   (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)){
				// 如果屏幕是因为超时关闭,或者用户主动关闭但不需要立即锁屏,
				// 则计划延迟显示锁屏
				doKeyguardLaterLocked();
			} else {
				// 否则,立即显示锁屏
				doKeyguardLocked(null);
			}
		}
		// 通知更新监控器屏幕已关闭,并传递关闭的原因
		KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
	}

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

  1. 当屏幕关闭时,这个方法会被调用。
  2. 如果设备设置为按下电源键立即锁定,或者设备没有设置安全措施(如PIN、密码、图案),则变量 lockImmediately 会被设置为 true。
  3. 根据 why 参数的值(表示屏幕关闭的原因),决定是否立即调用 doKeyguardLocked 或者延迟调用 doKeyguardLaterLocked(也会调用doKeyguardLocked )。

接下来关于doKeyguardLocked的处理,可以参考如下文章:

Android SystemUI组件(07)锁屏KeyguardViewMediator分析

参考其第二部分2.2 即可。

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

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

相关文章

【电路基础 · 2】电阻电路的等效变换(自用)

总览 1.电路的等效变换 1.1 电阻电路 1.2 等效变换是什么 1.3 线性电路和非线性电路 1.4 时变电路和非时变电路 1.5 二端网络&#xff08;一端口网络&#xff09;、四端网络&#xff08;二端口网络&#xff09;、六端网络&#xff08;三端口网络&#xff09; 1.6 两端电路的等…

每日一题:二分查找

文章目录 一、思路一&#xff1a;常规思路1、寻找固定值2、寻找左边界3、寻找右边界 二、思路二&#xff1a;红蓝法二分三、模板题1、二分查找2、在排序数组中查找元素的第一个和最后一个位置 二分查找&#xff0c;顾名思义&#xff0c;就是每次筛选能晒掉一半的数据。 二分查…

leetcode每日一题day22(24.10.2)——准时到达的列车最小度

思路&#xff1a;这种在有约束条件情况下&#xff0c;求最值或最符合要求的情况&#xff0c;首先是很容易想到&#xff0c;从时速为1开始往后找找到满足条件就输出&#xff0c;但这无疑工程量很大&#xff0c;每种可能的速度都要对列车数组进行遍历&#xff0c; 时间复杂度为C…

数据库第8章编程题2

10-1 查询选修某两门课程的学生&#xff08;MSSQL) 本题目要求编写SQL语句&#xff0c; 检索出 sc表中至少选修了’C001’与’C002’课程的学生学号。 提示&#xff1a;MSSQLServer 评测SQL语句。 表结构: 请在这里写定义表结构的SQL语句。例如&#xff1a; -- 学生选课成…

Pikachu-Cross-Site Scripting-存储型xss

存储型xss &#xff0c;随便输入点内容&#xff0c;都能保存下来&#xff1b;刷新后也不会丢失&#xff1b;输入特殊字符&#xff0c;也能原样返回&#xff1b; 查看代码&#xff0c;也可以看到输出结果直接原路返回&#xff0c;不做处理 构造payload <script>alert(1)…

深度学习:cGAN和pix2pix图像转换

cGAN和pix2pix的基础概念 cGAN cGAN是条件生成对抗网络&#xff08;Conditional Generative Adversarial Networks&#xff09;的简称。 它是一种基于基础GAN&#xff08;Generative Adversarial Networks&#xff09;架构的变体&#xff0c;通过给GAN模型引入额外的信息或条…

翔云 OCR:发票识别与验真

在数字化时代&#xff0c;高效处理大量文档和数据成为企业和个人的迫切需求。翔云 OCR 作为一款强大的光学字符识别工具&#xff0c;在发票识别及验真方面表现出色&#xff0c;为我们带来了极大的便利。 一、翔云 OCR 简介 翔云 OCR 是一款基于先进的人工智能技术开发的文字识别…

酒店智能门锁SDK接口pro[V10] 门锁校验C#-SAAS本地化-未来之窗行业应用跨平台架构

一、代码 int 酒店标识_int Convert.ToInt32(酒店标识);StringBuilder 锁号2024 new StringBuilder(8);//信息 "未知返回值&#xff1a;" bufCard_原始;GetGuestLockNoByCardDataStr_原始(酒店标识_int, bufCard_原始.ToString(), 锁号2024);StringBuilder 退…

Pie-饼图

参考文档&#xff1a;Pie - Pie_set_color - Document (pyecharts.org) 模板 from pyecharts import options as opts from pyecharts.charts import Pie from pyecharts.faker import Faker"""1-Pie() 创建Pie对象2-.add("", [list(z) for z in zi…

CSS 圆形边框与阴影

目录 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影

CSS盒子模型基础知识(23个案例+代码+效果图)

目录 1.边框样式 案例&#xff1a;制作一个边框为实线的正方形 1&#xff09;代码​编辑 2&#xff09;效果 案例&#xff1a;制作一个边框为虚线的正方形 1&#xff09;代码 2&#xff09;效果 案例&#xff1a;制作一个边框为点线的正方形 1&#xff09;代码 2&#xff09;效…

二分查找一>:在排序数组中查找元素的第一个和最后一个位置

1.题目&#xff1a; 2.解析:这里不能用传统二分&#xff0c;因为涉及范围&#xff0c;传统二分时间复杂度会降为O(N)&#xff0c;要做些改动。 步骤一&#xff1a;查找区间左端点 细节图&#xff1a; 步骤二&#xff1a;查找区间右端点&#xff1a; 细节图&#xff1a; 代码…

Cpp::STL—vector类的使用与理解(上)(10)

文章目录 前言一、vector的介绍三个原生指针的图示 二、vector的构造函数一个注意事项 二、vector的空间大小、调整函数size()capacity()empty()resize()reserve() 三、vector的增删查改push_back & pop_backinsert & erasefindswapfront & backoperator[ ] & …

JVM Class类文件结构

国庆节快乐 2024年10月2日17:49:22 目录 前言 magic 数 文件版本 使用JClassLib观察class文件 一般信息 接口 常量池 字段 方法 常量池计数器 常量池 类型 CONSTANT_Methodref_info CONSTANT_Class_info 类型结构总表 访问标志 类索引, …

信息安全工程师(30)认证概述

前言 认证&#xff0c;作为一种信用保证形式&#xff0c;是通过一系列的程序和标准来确认某人或某物的身份、资格、性能或质量的过程。其重要性不言而喻&#xff0c;是国家规范经济、促进发展的重要手段&#xff0c;也是政府保障产品、生态和人民生命财产安全的关键措施&#…

C语言 | Leetcode C语言题解之第452题用最少数量的箭引爆气球

题目&#xff1a; 题解&#xff1a; int cmp(void* _a, void* _b) {int *a *(int**)_a, *b *(int**)_b;return a[1] < b[1] ? -1 : 1; }int findMinArrowShots(int** points, int pointsSize, int* pointsColSize) {if (!pointsSize) {return 0;}qsort(points, pointsSi…

深度剖析音频剪辑免费工具的特色与优势

是热爱生活的伙伴或者想要记录美好声音的普通用户&#xff0c;都可能会需要对音频进行剪辑处理。而幸运的是&#xff0c;现在有许多优秀的音频剪辑软件提供了免费版本&#xff0c;让我们能够轻松地施展音频剪辑的魔法。接下来&#xff0c;就让我们一同深入了解这些音频剪辑免费…

【Docker】docker的存储

介绍 docker存储主要是涉及到3个方面&#xff1a; 第一个是容器启动时需要的镜像 镜像文件都是基于图层存储驱动来实现的&#xff0c;镜像图层都是只读层&#xff0c; 第二个是&#xff1a; 容器读写层&#xff0c; 容器启动后&#xff0c;docker会基于容器镜像的读层&…

VScode 自定义代码配色方案

vscode是一款高度自定义配置的编辑器, 我们来看看如何使用它自定义配色吧 首先自定义代码配色是什么呢? 看看我的代码界面 简而言之, 就是给你的代码的不同语义(类名, 函数名, 关键字, 变量)等设置不同的颜色, 使得代码的可读性变强. 其实很多主题已经给出了定制好的配色方案…

「C++系列」预处理器

【人工智能教程】&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站&#xff1a;【人工智能教程】 文章目录 一、预处理器1. 宏定义&#xff08;Macro Definition&#xff09;2…