【安卓12源码】Input子系统(1) - input启动流程

Android Input 体系中,大致有两种类型的事件:实体按键 key 事件,屏幕点击触摸事件。

如果根据事件类型的不同我们还能细分为基础实体按键的 key(power,volume up/down,recents,back,home),实体键盘按键,屏幕点击 (多点,单点),屏幕滑动等等的事件。

在 Android 整个 Input 体系中有三个格外重要的成员:Eventhub,InputReader,InputDispatcher。

它们分别担负着各自不同的职责,Eventhub 负责监听 / dev/input 产生 Input 事件,InputReader 负责从 Eventhub 读取事件,并将读取的事件发给 InputDispatcher,InputDispatcher 则根据实际的需要具体分发给当前手机获得焦点实际的 Window。

 inputmanagerservice启动原理

inpu子系统是运行在system 系统进程的

/frameworks/base/services/java/com/android/server/SystemServer.java

1325      private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
1326          t.traceBegin("startOtherServices");
。。。。。。
1471              t.traceBegin("StartInputManagerService");
// 1)创建 InputManagerService 对象
1472              inputManager = new InputManagerService(context);// 2)将其作为参数传入到wms 中
1488              wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
1489                      new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
// 增加这个input service,给客户端使用
1490              ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
1491                      DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
1492              ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
1493                      /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);// 3)启动 InputManagerService
1520              t.traceBegin("StartInputManager");
1521              inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
1522              inputManager.start();

1)创建 InputManagerService 对象

 /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

424      public InputManagerService(Context context) {
425          this.mContext = context;// 创建handler,在 DisplayThread 线程中处理
426          this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
427  
428          mStaticAssociations = loadStaticInputPortAssociations();
429          mUseDevInputEventForAudioJack =
430                  context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);// system 系统进程input,会打印下列的日志
431          Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
432                  + mUseDevInputEventForAudioJack);// 调用native 层的方法去初始化
433          mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
434  
435          String doubleTouchGestureEnablePath = context.getResources().getString(
436                  R.string.config_doubleTouchGestureEnableFile);
437          mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
438              new File(doubleTouchGestureEnablePath);
439  
440          LocalServices.addService(InputManagerInternal.class, new LocalService());
441      }

// 调用native 层的方法去初始化,looper 是 DisplayThread 线程

/frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

1488  static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
1489          jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
1490      sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
1491      if (messageQueue == nullptr) {
1492          jniThrowRuntimeException(env, "MessageQueue is not initialized.");
1493          return 0;
1494      }
1495  // 创建NativeInputManager 对象,looper 是DisplayThread 线程【再次说明】
1496      NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
1497              messageQueue->getLooper());
1498      im->incStrong(0);
1499      return reinterpret_cast<jlong>(im);
1500  }

创建NativeInputManager 对象

407  NativeInputManager::NativeInputManager(jobject contextObj,
408          jobject serviceObj, const sp<Looper>& looper) :// 缓存 mLooper
409          mLooper(looper), mInteractive(true) {
410      JNIEnv* env = jniEnv();
411  
412      mServiceObj = env->NewGlobalRef(serviceObj);
413  
414      {
415          AutoMutex _l(mLock);
416          mLocked.systemUiLightsOut = false;
417          mLocked.pointerSpeed = 0;
418          mLocked.pointerGesturesEnabled = true;
419          mLocked.showTouches = false;
420          mLocked.pointerCapture = false;
421          mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
422      }
423      mInteractive = true;
424  // 创建对象 InputManager,2个参数都是this
425      InputManager* im = new InputManager(this, this);
426      mInputManager = im;// 将其保存到 "inputflinger",给native 层调用
427      defaultServiceManager()->addService(String16("inputflinger"), im);
428  }

创建对象 InputManager,2个参数都是this
/frameworks/native/services/inputflinger/InputManager.cpp

53  InputManager::InputManager(
54          const sp<InputReaderPolicyInterface>& readerPolicy,
55          const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {// 1-1)创建 InputDispatcher对象
56      mDispatcher = createInputDispatcher(dispatcherPolicy);// 1-2)创建 InputClassifier 对象
57      mClassifier = new InputClassifier(mDispatcher);// 1-3)创建 InputReader 对象,传入了 InputClassifier对象
58      mReader = createInputReader(readerPolicy, mClassifier);
59  }
60  

// 1-1)创建 InputDispatcher对象
frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp

22  sp<InputDispatcherInterface> createInputDispatcher(
23          const sp<InputDispatcherPolicyInterface>& policy) {
24      return new android::inputdispatcher::InputDispatcher(policy);
25  }

 /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

509  InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)//mPolicy 是 NativeInputManager,可以通过此从native层传消息给java 层,比如通知没有焦点窗口510        : mPolicy(policy),
511          mPendingEvent(nullptr),
512          mLastDropReason(DropReason::NOT_DROPPED),
513          mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
514          mAppSwitchSawKeyDown(false),
515          mAppSwitchDueTime(LONG_LONG_MAX),
516          mNextUnblockedEvent(nullptr),
517          mDispatchEnabled(false),
518          mDispatchFrozen(false),
519          mInputFilterEnabled(false),
520          // mInTouchMode will be initialized by the WindowManager to the default device config.
521          // To avoid leaking stack in case that call never comes, and for tests,
522          // initialize it here anyways.
523          mInTouchMode(true),
524          mMaximumObscuringOpacityForTouch(1.0f),
525          mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
526          mFocusedWindowRequestedPointerCapture(false),
527          mWindowTokenWithPointerCapture(nullptr),
528          mLatencyAggregator(),
529          mLatencyTracker(&mLatencyAggregator),
530          mCompatService(getCompatService()) {
531      mLooper = new Looper(false);
532      mReporter = createInputReporter();
533  
534      mKeyRepeatState.lastKeyEntry = nullptr;
535  
536      policy->getDispatcherConfiguration(&mConfig);
537  }

// 1-2)创建 InputClassifier 对象

/frameworks/native/services/inputflinger/InputClassifier.cpp

 构造函数中传入的参数 mListener 是inputdispatcher。而 InputClassifier也作为参数传入给 InputReader。相当于 InputClassifier 是 沟通 InputReader和 inputdispatcher 的桥梁

348  InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
349        : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
350  

// 1-3)创建 InputReader 对象,传入了 InputClassifier对象

/frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp

23  sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
24                                             const sp<InputListenerInterface>& listener) {// 这里还创建了 EventHub,负责读取驱动的事件
25      return new InputReader(std::make_unique<EventHub>(), policy, listener);
26  }

InputReader 构造方法

/frameworks/native/services/inputflinger/reader/InputReader.cpp

43  InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
44                           const sp<InputReaderPolicyInterface>& policy,
45                           const sp<InputListenerInterface>& listener)
46        : mContext(this),
47          mEventHub(eventHub),
48          mPolicy(policy),
49          mGlobalMetaState(0),
50          mLedMetaState(AMETA_NUM_LOCK_ON),
51          mGeneration(1),
52          mNextInputDeviceId(END_RESERVED_ID),
53          mDisableVirtualKeysTimeout(LLONG_MIN),
54          mNextTimeout(LLONG_MAX),
55          mConfigurationChangesToRefresh(0) {// 创建 QueuedInputListener 对象,listener 是 InputClassifier.cpp
56      mQueuedListener = new QueuedInputListener(listener);
57  
58      { // acquire lock
59          std::scoped_lock _l(mLock);
60  
61          refreshConfigurationLocked(0);
62          updateGlobalMetaStateLocked();
63      } // release lock
64  }

/frameworks/native/services/inputflinger/InputListener.cpp

在调用 flush 后,可以将信息传递给 inputDispatcher 

315  QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
316          mInnerListener(innerListener) {
317  }367  void QueuedInputListener::flush() {
368      size_t count = mArgsQueue.size();
369      for (size_t i = 0; i < count; i++) {
370          NotifyArgs* args = mArgsQueue[i];
371          args->notify(mInnerListener);
372          delete args;
373      }
374      mArgsQueue.clear();
375  }

2)将其作为参数传入到wms 中

/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

1204      private WindowManagerService(Context context, InputManagerService inputManager,
1205              boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
1206              ActivityTaskManagerService atm, DisplayWindowSettingsProvider
1207              displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
1208              Supplier<Surface> surfaceFactory,
1209              Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
1210          installLock(this, INDEX_WINDOW);
1211          mGlobalLock = atm.getGlobalLock();
1212          mAtmService = atm;
1213          mContext = context;
1214          mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
1215          mAllowBootMessages = showBootMsgs;
1216          mOnlyCore = onlyCore;
1217          mLimitedAlphaCompositing = context.getResources().getBoolean(
1218                  com.android.internal.R.bool.config_sf_limitedAlpha);
1219          mHasPermanentDpad = context.getResources().getBoolean(
1220                  com.android.internal.R.bool.config_hasPermanentDpad);
1221          mInTouchMode = context.getResources().getBoolean(
1222                  com.android.internal.R.bool.config_defaultInTouchMode);
1223          inputManager.setInTouchMode(mInTouchMode);
1224          mDrawLockTimeoutMillis = context.getResources().getInteger(
1225                  com.android.internal.R.integer.config_drawLockTimeoutMillis);
1226          mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
1227                  com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
1228          mMaxUiWidth = context.getResources().getInteger(
1229                  com.android.internal.R.integer.config_maxUiWidth);
1230          mDisableTransitionAnimation = context.getResources().getBoolean(
1231                  com.android.internal.R.bool.config_disableTransitionAnimation);
1232          mPerDisplayFocusEnabled = context.getResources().getBoolean(
1233                  com.android.internal.R.bool.config_perDisplayFocusEnabled);
1234          mAssistantOnTopOfDream = context.getResources().getBoolean(
1235                  com.android.internal.R.bool.config_assistantOnTopOfDream);
1236  
1237          mLetterboxConfiguration = new LetterboxConfiguration(context);
1238  // 缓存了 inputManager
1239          mInputManager = inputManager; // Must be before createDisplayContentLocked.

3)启动 InputManagerService

InputManager 类继承自 InputManagerInterface 接口,是系统处理输入事件的核心。 InputManager 包含两个线程:

  • InputReader  Thread(称为“ InputReader”)读取并预处理原始输入事件EventHub->getEvents,应用策略
  • InputDispatcher  Thread(称为“ InputDispatcher”)线程在队列上等待新事件,然 后异步将其分配给应用程序。

首先InputManagerService 设置callback: setWindowManagerCallbacks

该callback是:

4746      final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);

/frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java

    public InputManagerCallback(WindowManagerService service) {mService = service;}// 看起来是和回调通知消息给 wms相关public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {mService.mAnrController.notifyAppUnresponsive(applicationHandle, "Application does not have a focused window");}

设置callback: setWindowManagerCallbacks

/frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

    public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {// 保证只有一个 callbackif (mWindowManagerCallbacks != null) {unregisterLidSwitchCallbackInternal(mWindowManagerCallbacks);}
// 缓存这个callbackmWindowManagerCallbacks = callbacks;registerLidSwitchCallbackInternal(mWindowManagerCallbacks);}

启动 InputManagerService

    public void start() {Slog.i(TAG, "Starting input manager");// native 去启动startnativeStart(mPtr);// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);registerPointerSpeedSettingObserver();registerShowTouchesSettingObserver();.........

nativeStart

/frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);// 获取到 InputManager 对象,调用其start 的方法status_t result = im->getInputManager()->start();if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}

 /frameworks/native/services/inputflinger/InputManager.cpp

status_t InputManager::start() {// 调用InputDispatcher 的start 方法status_t result = mDispatcher->start();if (result) {ALOGE("Could not start InputDispatcher thread due to error %d.", result);return result;}// 调用InputReader 的start 方法result = mReader->start();if (result) {ALOGE("Could not start InputReader due to error %d.", result);mDispatcher->stop();return result;}return OK;
}

// 调用InputDispatcher 的start 方法

/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}// 再系统进程中创建线程,名字为 "InputDispatcher"
// 创建 InputThread 线程,传入lambda循环函数dispatchOncemThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;
}=============/frameworks/native/services/inputflinger/InputThread.cpp// Implementation of Thread from libutils.
class InputThreadImpl : public Thread {
public:explicit InputThreadImpl(std::function<void()> loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::function<void()> mThreadLoop;// 会调用 threadLoop,执行loop方法bool threadLoop() override {mThreadLoop();return true;}
};} // namespace// 创建 InputThread
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake): mName(name), mThreadWake(wake) {// 创建InputThreadImpl对象,传入 loop函数mThread = new InputThreadImpl(loop);
// 开启线程循环。mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}

/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

执行loop 方法 

void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockstd::scoped_lock _l(mLock);mDispatcherIsAlive.notify_all();// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.if (!haveCommandsLocked()) {dispatchOnceInnerLocked(&nextWakeupTime);}// Run all pending commands if there are any.// If any commands were run then force the next poll to wake up immediately.if (runCommandsLockedInterruptible()) {nextWakeupTime = LONG_LONG_MIN;}// If we are still waiting for ack on some events,// we might have to wake up earlier to check if an app is anr'ing.const nsecs_t nextAnrCheck = processAnrsLocked();nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);// We are about to enter an infinitely long sleep, because we have no commands or// pending or queued eventsif (nextWakeupTime == LONG_LONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // release lock// Wait for callback or timeout or wake.  (make sure we round up, not down)nsecs_t currentTime = now();int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);mLooper->pollOnce(timeoutMillis);
}

// 调用InputReader 的start 方法

/frameworks/native/services/inputflinger/reader/InputReader.cpp

status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}// 同理,也是在系统进程中创建一个线程,名字为 InputReader
// 执行方法 loopOnce()mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}============
void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;std::vector<InputDeviceInfo> inputDevices;{ // acquire lockstd::scoped_lock _l(mLock);oldGeneration = mGeneration;timeoutMillis = -1;uint32_t changes = mConfigurationChangesToRefresh;if (changes) {mConfigurationChangesToRefresh = 0;timeoutMillis = 0;refreshConfigurationLocked(changes);} else if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);}} // release lock// 去从驱动中读取input 事件size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockstd::scoped_lock _l(mLock);mReaderIsAliveCondition.notify_all();if (count) {processEventsLocked(mEventBuffer, count);}

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

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

相关文章

KingbaseES数据库union的用法

数据库版本&#xff1a;KingbaseES V008R006C008B0014 文章目录如下 1. union的概念 2. union的语法 3. union的用法 3.1. 去重&#xff08;union&#xff09; 3.2. 不去重&#xff08;union all&#xff09; 3.3. 聚合运算 3.4. 异常案例 1. union的概念 UNION 是结构…

冷冻式压缩空气干燥机常见几种系统原理图

冷冻式压缩空气干燥机 我们以两种典型的设计流程图为例 1.干式蒸发型&#xff0c;这类冷干机是我们最为常见的设计型式。下图为deltech公司的典型流程图 此类设备各家设计不同的最大区别基本就是在换热器的结构型式上有比较大的区别。换热器主要有&#xff1a;管壳式、铝板换、…

typescript 配置精讲 | moduleResolution

大家好&#xff0c;我是17。 moduleResolution 是 typescript 模块配置中最重要的一个配置&#xff0c;所以 17 单拿出来讲一下。如果你去看文档还是挺复杂的&#xff0c;但如果不去深究细节&#xff0c;只想知道如何配置还是很简单的。3 分钟就能学会。 moduleResolution 的…

STM32无源蜂鸣器播放音乐

开发板&#xff1a;野火霸天虎V2 单片机&#xff1a;STM32F407ZGT6 开发软件&#xff1a;MDKSTM32CubeMX 文章目录 前言一、找一篇音乐的简谱二、确定音调三、确定节拍四、使用STM32CubeMX生成初始化代码五、代码分析 前言 本实验使用的是低电平触发的无源蜂鸣器 无源蜂鸣器是…

【模拟面试问答】深入解析力扣163题:缺失的区间(线性扫描与双指针法详解)

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

嵌入式0基础开始学习 ⅠC语言(1)数据类型

想学好嵌入式&#xff0c;那么就从c语言开始学习&#xff01; 0.问题引入 面对过程&#xff08;c语言&#xff09; 程序组成&#xff1a; 程序 算法 数据结构 计算机首先要解决数据保存的问题&#xff0c;在数据保存之前…

软考高级架构师:再工程、正向工程、设计恢复的区别

再工程&#xff08;Re-engineering&#xff09;、正向工程&#xff08;Forward Engineering&#xff09;、设计恢复&#xff08;Design Recovery&#xff09;是软件工程中的三个不同概念&#xff0c;各自有不同的目的和过程。以下是它们的区别&#xff1a; 再工程&#xff08;…

华为OD机试 - 寻找最富裕的小家庭(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

C语言对一阶指针 二阶指针的本质理解

代码&#xff1a; #include <stdio.h>char a 2; char* p &a; char** d &p;int main(){printf("a -> %d, &a -> %p\n", a, &a);printf("*p -> %d, p -> %p, &p -> %p\n", *p, p, &p);printf(&qu…

【JavaEE初阶】网络初识|局域网和广域网|交换机和路由器|IP地址|端口号

&#x1f4a1;推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击跳转到网站】 关键概念 1.局域网LAN和广域网WAN &#xff08;1&#xff09;局域⽹&#xff0c;即Local Area Network&#xff0…

嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

【代码随想录】动态规划经典题

前言 更详细的在大佬的代码随想录 (programmercarl.com) 本系列仅是简洁版笔记&#xff0c;为了之后方便观看 做题步骤 含义公式初始化顺序检查 确定dp数组以及下标的含义递推公式dp数组如何初始化遍历顺序打印dp数组&#xff08;看哪里有问题&#xff09; 斐波那契数 c…

乐理学习-音及音名

1. 我觉得练习题很重要。我要得到一个反馈 所以我想没学习完书中的一节就要把练习题做下来&#xff0c;虽然慢点也可以。 2. 做个小计划。 今天计算了一下学完《基本乐理-李重光》如果每天3张。也要80天干完&#xff0c;希望能有一天可以学习7张的速度的时候。 3. 练习记录…

STM32H7系统窗口看门狗 (WWDG)应用方法介绍

目录 概述 1 认识窗口看门狗 (WWDG) 1.1 窗口看门狗定义 1.2 WWDG 主要特性 2 WWDG 功能说明 2.1 WWDG框图 2.2 WWDG 内部信号 2.3 控制递减计数器 2.4 看门狗中断高级特性 2.5 如何设置看门狗超时 3 WWDG 寄存器 3.1 控制寄存器 (WWDG_CR) 3.2 配置寄存器 (W…

MicroLED:苹果对知识产权的影响

Yole的洞察揭示&#xff0c;MicroLED IP在经历了七年的爆炸式增长后&#xff0c;已然屹立于行业之巅。苹果公司&#xff0c;作为微LED领域的先行者&#xff0c;早在2014年便敏锐地捕捉到Luxvue这家初创公司的潜力&#xff0c;将其纳入麾下&#xff0c;引发了业界的广泛关注。然…

【Springboot系列】SpringBoot 中的日志如何工作的,看完这一篇就够了

文章目录 强烈推荐引言Spring Boot 中的日志是怎么工作日志框架选择配置文件日志级别自定义日志配置集成第三方日志库实时监控和日志管理 Log4j2工作原理分析1. 核心组件2. 配置文件3. Logger的继承和层次结构4. 日志事件处理流程5. 异步日志 总结强烈推荐专栏集锦写在最后 强烈…

每日练习之矩阵乘法——斐波那契公约数

斐波那契公约数 题目描述 运行代码 #include <iostream> #include <vector> using namespace std; const long long mod 1000000007;// 矩阵乘法函数 vector<vector<long long>> matrixMultiply(const vector<vector<long long>>& …

SpringBoot电脑商城项目(一)——项目环境搭建及用户注册功能的实现

静态资源及sql文件分享 链接&#xff1a;https://pan.baidu.com/s/1jWSRPf19GGK1_-z_sgfzTQ?pwdbdgy 提取码&#xff1a;bdgy 项目名称:store,表示商城结构:com.cy.store资源文件:resources文件夹下(static,templates)单元测试:test.com.cy.store 配置文件 application.ym…

【玩转google云】在GKE上部署 KubeSphere:详尽指南与步骤

目录 一、前言 二、准备工作 三、在 GKE上安装 KubeSphere 四、访问 KubeSphere 控制台

移动端特效

一&#xff0c;触屏事件 1.触屏touch事件 2.触摸事件对象 如果侦听的是一个DOM元素&#xff0c;他俩是一样的 如果手指离开了&#xff0c;不能侦听到前两者&#xff0c;能侦听到第三个 我们一般都是触摸元素&#xff0c;所以一般都是第二个比较常用 第一个&#xff1a;屏幕…