当前位置: 首页 > news >正文

Android7 Input(四)InputReader

概述

本文主要描述了Android Input框架中的InputReader的功能,InputReader模块的功能,总结成一句话就是InputReader获取输入设备的事件并将事件进行加工处理,然后传递给QueuedInputListener,最终QueuedInputListener将事件传递给InputDispatcher模块处理,事件处理的方向就是:

InputReader -> QueuedInputListener -> InputDispatcher

本文涉及的源码路径

frameworks/native/services/inputflinger/EventHub.cpp

frameworks/native/services/inputflinger/EventHub.h

frameworks/native/services/inputflinger/InputReader.cpp

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

InputReader的初始化

InputReader对象由InputManager创建如下所示:

InputManager::InputManager(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {/* 创建事件分发对象 */mDispatcher = new InputDispatcher(dispatcherPolicy);/* 创建事件获取对象 */mReader = new InputReader(eventHub, readerPolicy, mDispatcher);initialize();
}

继续深入InputReader类的构造函数,如下所示:

InputReader::InputReader(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) :......mConfigurationChangesToRefresh(0) {// 初始化事件监听器mQueuedListener = new QueuedInputListener(listener);......
}

Inputreader的构造函数比较简单,在这里我们关注构造函数的第一个参数和第三个参数。

eventHub: 获取事件来源的对象,InputReader通过EventHub获取输入设备上报的事件;

listener: InputReader处理完事件后的传递目的地,后面的章节有描述处理流程;

InputReader的运行

在InputManager的初始化过程中,会创建一个InputReadThread线程,如下所示:

void InputManager::initialize() {/* 创建事件获取线程 */mReaderThread = new InputReaderThread(mReader);......
}

InputManager初始化完成开始运行的时候会调用线程的运行方法,对InputReader来说最终调用InputReader::loopOnce, 如下所示:

void InputReader::loopOnce() {......size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lock......// 处理获取到的intput事件if (count) {processEventsLocked(mEventBuffer, count);}......} // release lock......// 刷新监听队列,将InputReader的事件处理结果,传递给InputDispatcher处理mQueuedListener->flush();
}

InputReader的事件获取

InputReader在Thread线程中调用EventHub的getEvents方法获取输入事件,如下所示:

 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

该方法将获取到input事件保存在mEventBuffer中,并返回获取到的事件个数

InputReader的事件处理

InputReader从EventHub获取到输入事件后,调用processEventsLocked方法,该方法的核心代码如下所示:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId = rawEvent->deviceId;//统计同一个设备上报的事件数while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT|| rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}
#if DEBUG_RAW_EVENTSALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif/*** 处理deviceID设备的上报的事件* @batchSize: 事件个数*/processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {// 处理EventHub上报的输入设备增加和删除switch (rawEvent->type) {case EventHubInterface::DEVICE_ADDED:addDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::DEVICE_REMOVED:removeDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN:handleConfigurationChangedLocked(rawEvent->when);break;default:ALOG_ASSERT(false); // can't happenbreak;}}count -= batchSize;rawEvent += batchSize;}
}

processEventsLocked:方法主要完成如下的功能:

1、处理系统已经存在的输入设备上报的事件;

2、处理系统输入设备的增加和删除事件;

设备添加和删除

本章节主要描述当系统增加一个输入设备时的InputReader处理流程,对于系统设备的删除和扫描的处理流程与增加处理流程类似,这里不再描述。下面我们从系统增加输入设备的处理流程进行描述。系统增加输入设备调用addDeviceLocked方法,addDeviceLocked的核心代码如下所示:

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {ssize_t deviceIndex = mDevices.indexOfKey(deviceId);// 设备已经增加进来了,直接返回if (deviceIndex >= 0) {ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);return;}......// 创建InputDevice设备管理对象// @deviceId: 设备ID// @classes: 输入设备类型InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);device->configure(when, &mConfig, 0);device->reset(when);......mDevices.add(deviceId, device);......
}

addDeviceLocked主要完成如下功能:

a、检测该设备是否已经增加过了,如果是,直接返回;

b、根据输入设备的类型,创建InputDevice设备;

c、将创建的InputDevice设备加入到InputReader设备管理容器中;

接着我们分析createDeviceLocked方法,后续我们主要分析多点触控设备的事件处理,因此我们只保留与多点触控相关的核心实现如下所示:

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,const InputDeviceIdentifier& identifier, uint32_t classes) {InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),controllerNumber, identifier, classes);......// 多点触控if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {device->addMapper(new MultiTouchInputMapper(device));} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {device->addMapper(new SingleTouchInputMapper(device));}return device;
}

createDeviceLocked方法的核心功能比较简单:

a、创建InputDevice设备;

b、根据输入设备的类型安装对应的事件处理器,Android系统可以处理的输入设备种类很多,对于每一类输入设备Android系统都抽象出了一个事件处理器处理该类设备上报的输入事件,对于多点触控设备来说,就是安装MultiTouchInputMapper事件处理器;

c、返回InputDevice设备;

d、这里没有描述InputMapper的实现机制,它的主要作用就是定义事件处理的接口,比如鼠标设备的处理使用CursorButtonAccumulator类,单点触控类设备使用SingleTouchInputMapper,多点触控设备使用MultiTouchInputMapper等,感兴趣的同学可以阅读Android源码去分析它们之间的关系;

设备上报事件

设备上报的输入事件处理函数是processEventsForDeviceLocked,该方法的核心实现,如下所示:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,const RawEvent* rawEvents, size_t count) {......device->process(rawEvents, count);
}

进一步调用InputDevice中的process方法,实现如下:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {......size_t numMappers = mMappers.size();for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {......} else {for (size_t i = 0; i < numMappers; i++) {InputMapper* mapper = mMappers[i];mapper->process(rawEvent);}}}
}

该方法的核心功能就是遍历每一个事件,调用设备自己注册的事件处理器进行处理,对于多点触控设备来说,调用如下的方法:

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {TouchInputMapper::process(rawEvent);/* 处理多点触控坐标信息 */mMultiTouchMotionAccumulator.process(rawEvent);
}

继续调用TouchInputMapper类中的process进行处理,该方法的核心实现,如下所示:

void TouchInputMapper::process(const RawEvent* rawEvent) {/* 鼠标按键事件处理 */mCursorButtonAccumulator.process(rawEvent);/* 鼠标滚轮事件处理 */mCursorScrollAccumulator.process(rawEvent);/* 触摸按键 */mTouchButtonAccumulator.process(rawEvent);// 当前事件全部上报处理完成,进行事件同步操作if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {sync(rawEvent->when);}
}

事件处理的具体细节,我们不再深入谈论,该方法当事件处理完成后,调用sync进行事件同步,核心如下所示:

void TouchInputMapper::sync(nsecs_t when) {......// Sync touch// 同步触摸坐标syncTouch(when, next);......processRawTouches(false /*timeout*/);
}

该方法会调用syncTouch这个虚拟方法,对于多点触控设备来说,最终调用MultiTouchInputMapper子类实现syncTouch, 这里不再详细展开描述。然后调用processRawTouches开始事件的处理,实现方法如下所示:

void TouchInputMapper::processRawTouches(bool timeout) {......const size_t N = mRawStatesPending.size();size_t count;// 遍历每一个上报的事件for(count = 0; count < N; count++) {const RawState& next = mRawStatesPending[count];......mCurrentRawState.copyFrom(next);if (mCurrentRawState.when < mLastRawState.when) {mCurrentRawState.when = mLastRawState.when;}cookAndDispatch(mCurrentRawState.when);}// 事件处理完成,清空mRawStatesPendingif (count != 0) {mRawStatesPending.removeItemsAt(0, count);}.......
}

接着调用cookAndDispatch方法,核心实现如下所示:

void TouchInputMapper::cookAndDispatch(nsecs_t when) {// Always start with a clean state.mCurrentCookedState.clear();......cookPointerData();......} else {// 触摸屏......if (!mCurrentMotionAborted) {dispatchButtonRelease(when, policyFlags);dispatchHoverExit(when, policyFlags);dispatchTouches(when, policyFlags);dispatchHoverEnterAndMove(when, policyFlags);dispatchButtonPress(when, policyFlags);}......}......
}

该方法中,我们为了叙述的简单,只保留了触摸屏幕相关的处理,cookPointerData方法的实现,这里不详细描述,主要就是处理坐标信息,对于触摸屏幕设备来说,就是调用dispatchTouches方法接着处理,核心实现如下所示:

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {......if (currentIdBits == lastIdBits) {if (!currentIdBits.isEmpty()) {......// 滑动dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,AMOTION_EVENT_EDGE_FLAG_NONE,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,currentIdBits, -1,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}} else {......// Dispatch pointer up events.// 触点抬起while (!upIdBits.isEmpty()) {uint32_t upId = upIdBits.clearFirstMarkedBit();dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,mLastCookedState.cookedPointerData.pointerProperties,mLastCookedState.cookedPointerData.pointerCoords,mLastCookedState.cookedPointerData.idToIndex,dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);dispatchedIdBits.clearBit(upId);}......if (moveNeeded && !moveIdBits.isEmpty()) {ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);}// Dispatch pointer down events using the new pointer locations.// 触点按下while (!downIdBits.isEmpty()) {......dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);}}
}

该方法中会处理三种触摸动作,滑动,抬起和按下,最终调用dispatchMotion方法进行处理,核心实现如下所示:

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,......float xPrecision, float yPrecision, nsecs_t downTime) {......// 根据触摸屏幕的坐标事件,初始化NotifyMotionArgs对象NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,action, actionButton, flags, metaState, buttonState, edgeFlags,mViewport.displayId, pointerCount, pointerProperties, pointerCoords,xPrecision, yPrecision, downTime);// 找到InputReader的Listen,在初始时指定,最终指向是QueuedInputListenergetListener()->notifyMotion(&args);
}

该方法的核心,就是构造一个NotifyMotionArgs,然后调用InputReader自己的listen中的方法,最终调用QueuedInputListener中的notifyMotion,核心实现如下:

// 加入到内部mArgsQueue队列中
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {mArgsQueue.push(new NotifyMotionArgs(*args));
}

该方法的实现非常简单,构造一个NotifyMotionArgs,然后加入mArgsQueue队列中。

TouchInputMapper方法的process处理流程方法,讲述完成,然后我们回到MultiTouchInputMapper,接着调用了mMultiTouchMotionAccumulator中的process方法,这里不再展开叙述。

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {TouchInputMapper::process(rawEvent);/* 处理多点触控坐标信息 */mMultiTouchMotionAccumulator.process(rawEvent);
}

至此,一个输入设备的事件处理流程讲述完成,最后,我们回到InputReader线程执行函数的,如下所示:

void InputReader::loopOnce() {......size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockAutoMutex _l(mLock);mReaderIsAliveCondition.broadcast();// 处理获取到的intput事件if (count) {processEventsLocked(mEventBuffer, count);}......} // release lock......// 刷新监听队列,将InputReader的事件处理结果,传递给InputDispatcher处理mQueuedListener->flush();
}

我们可以看到,设备上报的所有输入事件处理完成后,最后调用mQueuedListener的flush方法。这个方法的实现,下一个章节进行描述。

InputReader事件传递

上一个章节,我们描述了InputReader处理输入设备上报事件的处理流程,当InputReader处理完上报的事件后调用mQueuedListener的flush,该方法的实现如下所示:

void QueuedInputListener::flush() {size_t count = mArgsQueue.size();for (size_t i = 0; i < count; i++) {NotifyArgs* args = mArgsQueue[i];args->notify(mInnerListener);delete args;}mArgsQueue.clear();
}

该方法的实现比较简单,调用notify虚拟方法,对于触摸设备来说就是调用子类实现如下所示:

// 直接传递给InputDispatcher
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {listener->notifyMotion(this);
}

直接调用listener的notifyMotion进行处理,这里的listener其实就是InputDispatcher,这在InputReader初始化的时候进行传递的,如下所示:

InputManager::InputManager(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {/* 创建事件分发对象 */mDispatcher = new InputDispatcher(dispatcherPolicy);/* 创建事件获取对象 */mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
}InputReader::InputReader(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) :mContext(this), mEventHub(eventHub), mPolicy(policy),mGlobalMetaState(0), mGeneration(1),mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),mConfigurationChangesToRefresh(0) {// 初始化事件监听器mQueuedListener = new QueuedInputListener(listener);{ // acquire lockAutoMutex _l(mLock);refreshConfigurationLocked(0);updateGlobalMetaStateLocked();} // release lock
}

将InputDispatcher当做QueuedInputListener的listener。最终listener->notifyMotion(this)调用到,如下所示:

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
......
}

这里不再详解展开描述,在InputDispatcher模块进行详细描述。

总结

本文描述了InputReader对EventHub事件的处理,InputReader对于每一个不同类型的输入设备调用对应的事件处理器进行处理,当事件处理完成后最终将处理结果传递给InputDispatcher。下一章我们描述InputDispatcher的功能。

相关文章:

Android7 Input(四)InputReader

概述 本文主要描述了Android Input框架中的InputReader的功能&#xff0c;InputReader模块的功能&#xff0c;总结成一句话就是InputReader获取输入设备的事件并将事件进行加工处理&#xff0c;然后传递给QueuedInputListener&#xff0c;最终QueuedInputListener将事件传递给…...

游戏报错?MFC140.dll怎么安装才能解决问题?提供多种MFC140.dll丢失修复方案

MFC140.dll 是 Microsoft Visual C 2015 运行库的重要组成部分&#xff0c;许多软件和游戏依赖它才能正常运行。如果你的电脑提示 "MFC140.dll 丢失" 或 "MFC140.dll 未找到"&#xff0c;说明系统缺少该文件&#xff0c;导致程序无法启动。本文将详细介绍 …...

寻找最大美丽数

# 输入&#xff1a;nums1 [4,2,1,5,3], nums2 [10,20,30,40,50], k 2 # 输出&#xff1a;[80,30,0,80,50] import random class Solution:def findMaxSum(self, nums1, nums2, k):hash_table []sum1 0data []print(**31,\n,\t数据)for key,values in enumerate(nums1):da…...

[Linux]进程地址空间

前言 我们在学习C语言期间&#xff0c;经常可以提及到这些区域&#xff0c;有一个问题&#xff1a;这里的地址空间是内存吗&#xff1f;答案是这里的地址空间并不是内存。这里的地址空间是进程地址空间&#xff0c;下面我们就讲解进程地址空间。 这段空间中自下而上&#xff…...

dfs和bfs算法

DFS&#xff08;深度优先搜索&#xff0c;Depth-First Search&#xff09;和 BFS&#xff08;广度优先搜索&#xff0c;Breadth-First Search&#xff09;是图遍历或搜索算法中的两种基本方法。它们在探索图的节点时采用不同的策略&#xff0c;适用于不同的场景。 ### 深度优先…...

跨站请求是什么?

介绍 跨站请求&#xff08;Cross-Site Request&#xff09;通常是指浏览器在访问一个网站时&#xff0c;向另一个域名的网站发送请求的行为。这个概念在 Web 安全中非常重要&#xff0c;尤其是在涉及到“跨站请求伪造&#xff08;CSRF&#xff09;”和“跨域资源共享&#xff…...

【深度学习与大模型基础】第9章-条件概率以及条件概率的链式法则

简单理解条件概率 条件概率就是在已知某件事发生的情况下&#xff0c;另一件事发生的概率。用数学符号表示就是&#xff1a; P(A|B) 在B发生的前提下&#xff0c;A发生的概率。 计算机例子&#xff1a;垃圾邮件过滤 假设你写了一个程序来自动判断邮件是否是垃圾邮件&#xf…...

C++: 获取auto的实际类型

auto a "hello";auto* b "hello";auto& c "hello";上述 a, b, c 类型分别是什么&#xff1f; 在不使用 IDE 提供的 inlay hints 情况下&#xff0c; 可以编译期获取&#xff0c;然后运行时打印出来: 方法&#xff1a; 用 decltype(var)…...

谷歌开源代理开发工具包(Agent Development Kit,ADK):让多智能体应用的构建变得更简

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

揭开人工智能与机器学习的神秘面纱:开发者的视角

李升伟 编译 人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;早已不再是空洞的流行语——它们正在彻底改变我们构建软件、做出决策以及与技术互动的方式。无论是自动化重复性任务&#xff0c;还是驱动自动驾驶汽车&#xff0c;AI/ML都是现代创新的核…...

35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)

一、线程池概述 1、线程池的优势 线程池是一种线程使用模式&#xff0c;线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能&#xff0c;而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务&#xff0c;这避免了在处理短时间任务时创建与…...

【NumPy科学计算:高性能数组操作核心指南】

目录 前言&#xff1a;技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解关键技术模块技术选型对比 二、实战演示环境配置要求核心代码实现运行结果验证 三、性能对比测试方法论量化数据对比结果分析 四、最佳实践推荐方案 ✅常见错误 ❌调试技…...

软考 系统架构设计师系列知识点之杂项集萃(50)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;49&#xff09; 第78题 著作权中&#xff0c;&#xff08;&#xff09;的保护期不受限制。 A. 发表权 B. 发行权 C. 署名权 D. 展览权 正确答案&#xff1a;C。 所属知识点&#xff1a;旧版…...

实现定长的内存池

池化技术 所谓的池化技术&#xff0c;就是程序预先向系统申请过量的资源&#xff0c;然后自己管理起来&#xff0c;以备不时之需。这个操作的价值就是&#xff0c;如果申请与释放资源的开销较大&#xff0c;提前申请资源并在使用后并不释放而是重复利用&#xff0c;能够提高程序…...

定制一款国密浏览器(7):铜锁和BoringSSL

上一章简单介绍了一下国密算法,本章开始进入实战,进行国密算法的移植。算法的移植以铜锁为蓝本,移植到 BoringSSL 中。 BoringSSL 也是由 OpenSSL fork 而来,那能否修改 Chromium 的源码,使用铜锁库呢?这种方式我也考虑并尝试过,最后发现两者的接口差别太大,Chromium …...

Docker 安装CRMEB陀螺匠教程

首先下载代码到服务器中&#xff0c;打开终端&#xff0c;并切换到项目源码根目录&#xff1a; 通过 Docker compose 启动项目 第一次启动时需要拉取和打包相关镜像&#xff0c;所需时长视网络情况而定&#xff0c;需耐心等待。 配置反向代理 参考 Nginx 配置 Nginx 反向代…...

Java中的static都能用来修饰什么?

在Java编程语言中&#xff0c;static关键字是非常重要的修饰符&#xff0c;可以用于多种不同的地方。可用来修饰变量、方法、代码块以及类。 1.静态变量 定义&#xff1a;静态变量属于类本身&#xff0c;而不是类的任何特定实例&#xff08;new出来的对象&#xff09;。 特点&a…...

词法分析器设计实验

掌握生成词法分析器的方法&#xff0c;加深对词法分析原理的理解。掌握设计、编制并调试词法分析程序的思想和方法。本实验是高级语言程序设计、数据结构和编译原理中词法分析原理等知识的综合。 【实验内容及要求】完善以下代码(红色标注处)并加上注释(蓝色标注处) int Getsym…...

matlab求和∑函数方程编程?

matlab求和∑函数方程编程&#xff1f; 一 题目&#xff1a;求下列函数方程式的和 二&#xff1a;代码如下&#xff1a; >> sum_result 0; % 初始化求和变量 for x 1:10 % 设…...

Vue3.5 企业级管理系统实战(十四):动态主题切换

动态主题切换是针对用户体验的常见的功能之一&#xff0c;我们可以自己实现如暗黑模式、明亮模式的切换&#xff0c;也可以利用 Element Plus 默认支持的强大动态主题方案实现。这里我们探讨的是后者通过 CSS 变量设置的方案。 1 组件准备 1.1 修改 Navbar 组件 在 src/layo…...

Python中for循环及其相关函数range(), zip(), enumerate()等

一、Python中的for循环及其相关函数 Python的for循环是算法竞赛中最常用的迭代工具之一&#xff0c;因其简洁和灵活性非常适合快速实现逻辑。以下详细讲解for循环及相关函数在竞赛中的使用场景。 1. for循环基本语法 Python的for循环用于遍历可迭代对象&#xff08;如列表、…...

数据结构与算法——链表OJ题详解(2)

文章目录 一、前言二、OJ续享2.1相交链表2.2环形链表12.2环形链表2 三、总结 一、前言 哦了兄弟们&#xff0c;咱们上次在详解链表OJ题的时候&#xff0c;有一部分OJ题呢up并没有整理完&#xff0c;这一个星期呢&#xff0c;up也是在不断的学习并且沉淀着&#xff0c;也是终于…...

免费送源码:Java+ssm+MySQL 基于PHP在线考试系统的设计与实现 计算机毕业设计原创定制

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对在线考试等问题&#xff0c;对如何通过计算…...

Android之JNI详解

Android之JNI详解 简介创建项目注册动态注册静态注册 关键词解读基础数据类型引用java对象JNI引用与释放cmake配置文件 简介 JNI&#xff08;Java Native Interface&#xff09; 是 Java 提供的一种编程框架&#xff0c;用于在 Java 应用程序中调用和与用其他编程语言&#xf…...

React Hooks: useRef,useCallback,useMemo用法详解

1. useRef&#xff08;保存引用值&#xff09; useRef 通常用于保存“不会参与 UI 渲染&#xff0c;但生命周期要长”的对象引用&#xff0c;比如获取 DOM、保存定时器 ID、WebSocket等。 新建useRef.js组件&#xff0c;写入代码&#xff1a; import React, { useRef, useSt…...

Java基础知识

概念 请介绍全局变量和局部变量的区别 Java中的变量分为成员变量和局部变量&#xff0c;它们的区别如下&#xff1a; 成员变量&#xff1a; 1. 成员变量是在类的范围里定义的变量&#xff1b; 2. 成员变量有默认初始值&#xff1b; 3. 未被static修饰的成员变量也叫…...

体验智能体构建过程:从零开始构建Agent

1. 什么是智能体&#xff1f; 智能体&#xff08;Agents&#xff09;是一种能够感知环境、做出决策并采取行动来实现特定目标的自主实体。智能体的复杂程度各不相同&#xff0c;从简单的响应式智能体&#xff08;对刺激直接做出反应&#xff09;到更高级的智能体&#xff08;能…...

如何从项目目标到成功标准:构建可量化、可落地的项目评估体系

引言 在项目管理领域&#xff0c;"项目成功"的定义往往比表面看起来更复杂。根据PMI的行业报告&#xff0c;67%的项目失败源于目标与成功标准的不匹配。当项目团队仅关注"按时交付"或"预算达标"时&#xff0c;常会忽视真正的价值创造。本文将通…...

大模型论文:Language Models are Few-Shot Learners(GPT3)

大模型论文&#xff1a;Language Models are Few-Shot Learners(GPT3) 文章地址&#xff1a;https://proceedings.neurips.cc/paper_files/paper/2020/file/1457c0d6bfcb4967418bfb8ac142f64a-Paper.pdf 一、摘要 我们证明了&#xff0c;扩大语言模型的规模在任务无关的 few…...

驱动学习专栏--字符设备驱动篇--1_chrdevbase

字符设备驱动简介 字符设备是 Linux 驱动中最基本的一类设备驱动&#xff0c;字符设备就是一个一个字节&#xff0c;按照字节 流进行读写操作的设备&#xff0c;读写数据是分先后顺序的。比如我们最常见的点灯、按键、 IIC 、 SPI &#xff0c; LCD 等等都是字符设备&…...

muduo库源码分析: TcpConnection

一. 主要成员: socket_&#xff1a;用于保存已连接套接字文件描述符。channel_&#xff1a;封装了上面的socket_及其各类事件的处理函数&#xff08;读、写、错误、关闭等事件处理函数&#xff09;。这个Channel中保存的各类事件的处理函数是在TcpConnection对象构造函数中注册…...

【SLAM】ubuntu 18.04 编译安装 OpenCV 3.2.0 时出现哈希错误

本文首发于❄慕雪的寒舍 1. 前言 1.1. 问题说明 在amd64的ubuntu 18.04 desktop上编译安装 OpenCV 3.2.0 的时候&#xff0c;我遇到了cmake构建错误。错误的核心报错如下 for file: [/home/king/slam/pkg/opencv-3.2.0/3rdparty/ippicv/downloads/linux-808b791a6eac9ed78d32…...

挂马漏洞 asp连接冰蝎webshell (附webshell源码 仅限学习研究)

目录 ASP WebShell代码 代码说明&#xff1a; 部署步骤&#xff1a; 使用冰蝎客户端连接&#xff1a; 注意事项&#xff1a; ASP WebShell代码 <% 冰蝎连接密码&#xff08;需与冰蝎客户端配置一致&#xff09; Dim key key "mysecretkey123" 自定义密码…...

Grafana将弃用AngularJS-我们该如何迁移

AngularJS 弃用时间线 AngularJS 支持已在 Grafana 9 中正式弃用。在 2024 年 5 月发布的 Grafana 11 中&#xff0c;所有 Grafana Cloud 和自托管安装默认关闭该功能。到 Grafana 12 版本时&#xff0c;将完全移除对 AngularJS 的支持&#xff0c;包括配置参数开关 angular_s…...

基于单片机的病房呼叫系统设计

2.1 总体方案设计 本课题为基于单片机的病房呼叫系统设计&#xff0c;在此将整个系统架构设计如图2.1所示&#xff0c;在此采用八个按键模拟8个不同的病房号&#xff0c;再通过8个LED指示灯对病房号的状态进行指示&#xff0c;当用户按键按键时&#xff0c;相应的LED灯会点亮…...

简述一下Unity的UnityWebRequest

UnityWebRequest是Unity引擎中用于处理网络请求的强大工具&#xff0c;尤其适用于与Web服务器进行交互&#xff0c;比如获取数据、上传文件或下载资源等。相较于旧版的WWW类&#xff0c;UnityWebRequest提供了更灵活、更高效的API&#xff0c;支持多种HTTP方法&#xff0c;并能…...

文件操作和IO - 2

目录 Java 中操作文件 File 概述 属性 构造方法 方法 getParent getName getPath getAbsolutePath getCanonicalPath exists isFile isDirectory createNewFile delete deleteOnExit list listFiles mkdir mkdirs 完&#xff01; Java 中操作文件 Java 对于文件操…...

⑪数据中心网络M-LAG实战

一、DeviceA-M-LAG-Mater配置 1、M-LAG 系统配置。 # m-lag mad exclude interface GigabitEthernet1/0/7 m-lag mad exclude interface Vlan-interface100 m-lag mad exclude interface Vlan-interface101 m-lag system-mac 0002-0002-0002 m-lag system-number 1 m-la…...

Win10系统安装WSL2-Ubuntu, 并使用VScode开始工作

本教程基于博主当前需要使用 WSL2(Windows Subsystem for Linux 2) 而编写&#xff0c;将自己使用的经过分享给大家。有什么意见建议敬请大家批评指正。此过程需要打开 Microsoft Store 话不多说&#xff0c;立即开始~ 文章目录 1. 检查系统版本2. 启动 WSL 功能3. 安装Ubuntu4…...

Node.js种cluster模块详解

Node.js 中 cluster 模块全部 API 详解 1. 模块属性 const cluster require(cluster);// 1. isMaster // 判断当前进程是否为主进程 console.log(是否为主进程:, cluster.isMaster);// 2. isWorker // 判断当前进程是否为工作进程 console.log(是否为工作进程:, cluster.isW…...

Window 10使用WSL2搭建Linux版Android Studio应用开发环境

一、背景 公司基于高通SA8155、SA8295等车载芯片进行座舱系统开发,使用repo统一管理系统及应用源码仓库。 Android应用端使用Ubuntu环境Android Studio进行开发,使用repo进行平台性管理,包含所有应用仓库。由于gradle配置使用了linux下文件软链接,无法直接使用Window环境…...

PostgreSQL 的统计信息

PostgreSQL 的统计信息 PostgreSQL 的统计信息是查询优化和性能调优的基础&#xff0c;系统通过多种统计信息来评估数据分布和访问模式&#xff0c;从而生成高效的执行计划。 一 统计信息类型与用途 1.1 核心统计类别 统计类型存储位置主要用途更新机制表和索引扫描统计pg_…...

【Linux】Linux基础指令

Linux系统初步介绍 Linux是一种自由和开放源代码的类UNIX操作系统,该操作系统的内核由林纳斯托瓦兹在1991年首次发布,之后,在加上用户空间的应用程序之后,就成为了Linux操作系统&#xff61; 严格来讲,Linux 只是操作系统内核本身,但通常采用“Linux内核”来表达该意思&#…...

【SLAM】在ORB_SLAM2的ROS模式下使用RealSense D435相机

本文介绍了如何在ORB_SLAM2项目中使用RealSense D435相机作为RGB-D输入源&#xff0c;包括ROS下启动D435相机、ORB_SLAM2订阅Topic、ORB_SLAM2读取realsense-viewer录制的rosbag文件等步骤。。 本文首发于❄慕雪的寒舍 1. 前言 先前已经编写了如何用TUM数据集运行ORB_SLAM3以及…...

scapy使用

https://scapy.readthedocs.io/en/latest/introduction.html 在 Mac 上使用 Scapy 抓取特定 IP 的流量并保存到 a.pcap 的步骤如下&#xff1a; 步骤 1&#xff1a;安装 Scapy 在终端中执行以下命令安装&#xff1a; pip install scapy步骤 2&#xff1a;创建 Python 脚本 …...

C2000 系统控制(4) — CPU Memory

CPU 内存 内存控制器 在 C2000 实时微控制器上&#xff0c;RAM 具有不同的特性。这些特性包括&#xff1a; CPU 专用&#xff1a;M0、M1 RAMCPU 和 CLA 共享&#xff1a;LSx RAMCPU、DMA 和 HIC 共享&#xff1a;GSx RAM用于在处理器之间发送和接收消息&#xff1a;MSG RAM …...

Linux网络编程——详解网络层IP协议、网段划分、路由

目录 一、前言 二、IP协议的认识 1、什么是IP协议&#xff1f; 2、IP协议报头 三、网段划分 1、初步认识IP与路由 2、IP地址 I、DHCP动态主机配置协议 3、IP地址的划分 I、CIDR设计 II、子网数目的计算 III、子网掩码的确定 四、特殊的IP地址 五、IP地址的数量限…...

解析医疗器械三大文档:DHF、DMR与DHR

医疗器械的 DHF、DMR 和 DHR 是质量管理体系&#xff08;QMS&#xff09;中的核心文件&#xff0c;贯穿产品全生命周期&#xff0c; 确保医疗器械的安全性、有效性和合规性。 一、三大文件的定义与法规依据 缩写全称法规依据&#xff08;以 FDA 为例&#xff09;核心目的DHF…...

SQL 全文检索原理

全文检索(Full-Text Search)是SQL中用于高效搜索文本数据的技术&#xff0c;与传统的LIKE操作或简单字符串比较相比&#xff0c;它能提供更强大、更灵活的文本搜索能力。 基本概念 全文检索的核心思想是将文本内容分解为可索引的单元(通常是词或词组)&#xff0c;然后建立倒排…...

基于Rosen梯度投影法的约束优化问题求解及MATLAB实现

摘要 在工程优化、经济建模等领域&#xff0c;约束优化问题普遍存在&#xff0c;其核心是在满足线性或非线性约束条件下求解目标函数的极值。本文聚焦Rosen梯度投影法&#xff0c;系统阐述其算法原理、实现步骤及MATLAB编程方法。 关键词&#xff1a;Rosen梯度投影法&#xf…...