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

Android14 InputManager-InputReader的处理

IMS启动时会调用InputReader.start()方法

InputReader.cpp
status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}

当线程开始运行后,将会在内建的线程循环中不断地调用threadLoop(),直到此函数返回false,则退出线程循环,从而结束线程。

InputReader的一次线程循环的工作思路非常清晰,一共三步:

□首先从EventHub中抽取未处理的事件列表。这些事件分为两类,一类是从设备节点中读取的原始输入事件,另一类则是输入设备可用性变化事件,简称为设备事件。

□通过processEventsLocked()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中。

□所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。

void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;// Copy some state so that we can access it outside the lock later.bool inputDevicesChanged = false;std::vector<InputDeviceInfo> inputDevices;std::list<NotifyArgs> notifyArgs;{ // acquire lockstd::scoped_lock _l(mLock);oldGeneration = mGeneration;timeoutMillis = -1;auto changes = mConfigurationChangesToRefresh;if (changes.any()) {mConfigurationChangesToRefresh.clear();timeoutMillis = 0;refreshConfigurationLocked(changes);} else if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);}} // release lockstd::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);{ // acquire lockstd::scoped_lock _l(mLock);mReaderIsAliveCondition.notify_all();
// 如果有事件信息,调用processEventsLocked()函数对事件进行加工处理if (!events.empty()) {notifyArgs += processEventsLocked(events.data(), events.size());}if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);if (now >= mNextTimeout) {if (debugRawEvents()) {ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);}mNextTimeout = LLONG_MAX;notifyArgs += timeoutExpiredLocked(now);}}if (oldGeneration != mGeneration) {inputDevicesChanged = true;inputDevices = getInputDevicesLocked();notifyArgs.emplace_back(NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});}} // release lock// Send out a message that the describes the changed input devices.if (inputDevicesChanged) {mPolicy->notifyInputDevicesChanged(inputDevices);}// Notify the policy of the start of every new stylus gesture outside the lock.for (const auto& args : notifyArgs) {const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);}}notifyAll(std::move(notifyArgs));// Flush queued events out to the listener.// This must happen outside of the lock because the listener could potentially call// back into the InputReader's methods, such as getScanCodeState, or become blocked// on another thread similarly waiting to acquire the InputReader lock thereby// resulting in a deadlock.  This situation is actually quite plausible because the// listener is actually the input dispatcher, which calls into the window manager,// which occasionally calls into the input reader.mQueuedListener.flush();
}

processEventsLocked()会分别处理原始输入事件与设备增删事件。

对于原始输入事件,由于EventHub会将属于同一输入设备的原始输入事件放在一起,因此processEventsLocked()可以使processEventsForDeviceLocked()同时处理来自同一输入设备的一批事件。

std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {std::list<NotifyArgs> out;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 (debugRawEvents()) {ALOGD("BatchSize: %zu Count: %zu", batchSize, count);}out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {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;}return out;
}
std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,const RawEvent* rawEvents,size_t count) {auto deviceIt = mDevices.find(eventHubId);if (deviceIt == mDevices.end()) {ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);return {};}std::shared_ptr<InputDevice>& device = deviceIt->second;if (device->isIgnored()) {// ALOGD("Discarding event for ignored deviceId %d.", deviceId);return {};}return device->process(rawEvents, count);
}
InputDevice.cpp
std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {// Process all of the events in order for each mapper.// We cannot simply ask each mapper to process them in bulk because mappers may// have side-effects that must be interleaved.  For example, joystick movement events and// gamepad button presses are handled by different mappers but they should be dispatched// in the order received.std::list<NotifyArgs> out;for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {if (debugRawEvents()) {const auto [type, code, value] =InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code,rawEvent->value);ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64,rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when);}if (mDropUntilNextSync) {if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");} else {ALOGD_IF(debugRawEvents(),"Dropped input event while waiting for next input sync.");}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());mDropUntilNextSync = true;out += reset(rawEvent->when);} else {for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {out += mapper.process(rawEvent);});}--count;}return out;
}

InputMapper是InputReader中实际进行原始输入事件加工的场所,它有一系列的子类,分别用于加工不同类型的原始输入事件。而InputDevice的process()函数使用InputMapper的方式是一个简化了的职责链(chain of responsibility)设计模式。InputDevice不需要知道哪一个InputMapper可以处理一个原始输入事件,只须将一个事件逐个交给每一个InputMapper尝试处理,如果InputMapper可以接受这个事件则处理之,否则什么都不做。

根据设备的类型,将设备做如下分配

InputMapper的种类实在很多,对所有类型都进行详细分析并不现实。选择KeyboardInputMapper和MultiTouchInputMapper两个常用并且具有代表性的InputMapper进行探讨

键盘事件的处理

KeyboardInputMapper的process()函数比较简单

□通过processKey()按键事件做进一步处理。

mQueuedListener.flush();

根据扫描码获取虚拟键值以及功能值后,KeyboardInputMapper::process()调用了processKey()函数对按键事件做进一步处理。

将键盘信息转化为NotifyKeyArgs,返回到loopOnce的loopOnce中,通知事件分发

触摸事件的处理

触摸事件的处理

std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);mMultiTouchMotionAccumulator.process(rawEvent);return out;
}

事件处理完成后回到最初的

mQueuedListener.flush(); // 将事件交给inputDispatcher的过程

 

InputDispatcher继承了InputDispatcherInterface接口
class InputDispatcher : public android::InputDispatcherInterface {
void InputListenerInterface::notify(const NotifyArgs& generalArgs) {Visitor v{[&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },[&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },[&](const NotifyKeyArgs& args) { notifyKey(args); },[&](const NotifyMotionArgs& args) { notifyMotion(args); },[&](const NotifySwitchArgs& args) { notifySwitch(args); },[&](const NotifySensorArgs& args) { notifySensor(args); },[&](const NotifyVibratorStateArgs& args) { notifyVibratorState(args); },[&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(args); },[&](const NotifyPointerCaptureChangedArgs& args) { notifyPointerCaptureChanged(args); },};std::visit(v, generalArgs);
}

以motion事件为例

void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {if (debugInboundEventDetails()) {ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, ""displayId=%" PRId32 ", policyFlags=0x%x, ""action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, ""edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, ""yCursorPosition=%f, downTime=%" PRId64,args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,args.downTime);for (uint32_t i = 0; i < args.pointerCount; i++) {ALOGD("  Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, ""touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",i, args.pointerProperties[i].id,ftl::enum_string(args.pointerProperties[i].toolType).c_str(),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));}}Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,args.pointerCount, args.pointerProperties);if (!motionCheck.ok()) {LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();return;}uint32_t policyFlags = args.policyFlags;policyFlags |= POLICY_FLAG_TRUSTED;android::base::Timer t;mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",std::to_string(t.duration().count()).c_str());}bool needWake = false;{ // acquire lockmLock.lock();if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {// Set the flag anyway if we already have an ongoing gesture. That would allow us to// complete the processing of the current stroke.const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);if (touchStateIt != mTouchStatesByDisplay.end()) {const TouchState& touchState = touchStateIt->second;if (touchState.deviceId == args.deviceId && touchState.isDown()) {policyFlags |= POLICY_FLAG_PASS_TO_USER;}}}if (shouldSendMotionToInputFilterLocked(args)) {ui::Transform displayTransform;if (const auto it = mDisplayInfos.find(args.displayId); it != mDisplayInfos.end()) {displayTransform = it->second.transform;}mLock.unlock();MotionEvent event;event.initialize(args.id, args.deviceId, args.source, args.displayId, INVALID_HMAC,args.action, args.actionButton, args.flags, args.edgeFlags,args.metaState, args.buttonState, args.classification,displayTransform, args.xPrecision, args.yPrecision,args.xCursorPosition, args.yCursorPosition, displayTransform,args.downTime, args.eventTime, args.pointerCount,args.pointerProperties, args.pointerCoords);policyFlags |= POLICY_FLAG_FILTERED;if (!mPolicy.filterInputEvent(event, policyFlags)) {return; // event was consumed by the filter}mLock.lock();}// Just enqueue a new motion event.std::unique_ptr<MotionEntry> newEntry =std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,args.displayId, policyFlags, args.action,args.actionButton, args.flags, args.metaState,args.buttonState, args.classification, args.edgeFlags,args.xPrecision, args.yPrecision,args.xCursorPosition, args.yCursorPosition,args.downTime, args.pointerCount,args.pointerProperties, args.pointerCoords);if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&!mInputFilterEnabled) {const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);}needWake = enqueueInboundEventLocked(std::move(newEntry));mLock.unlock();} // release lockif (needWake) {mLooper->wake();}
}

到达InputDispatcher的Motion事件被保存在MotionEntry类中

bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {bool needWake = mInboundQueue.empty();mInboundQueue.push_back(std::move(newEntry));EventEntry& entry = *(mInboundQueue.back());traceInboundQueueLengthLocked();switch (entry.type) {case EventEntry::Type::KEY: {LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,"Unexpected untrusted event.");// Optimize app switch latency.// If the application takes too long to catch up then we drop all events preceding// the app switch key.const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);if (isAppSwitchKeyEvent(keyEntry)) {if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {mAppSwitchSawKeyDown = true;} else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {if (mAppSwitchSawKeyDown) {if (DEBUG_APP_SWITCH) {ALOGD("App switch is pending!");}mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;mAppSwitchSawKeyDown = false;needWake = true;}}}// If a new up event comes in, and the pending event with same key code has been asked// to try again later because of the policy. We have to reset the intercept key wake up// time for it may have been handled in the policy and could be dropped.if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent &&mPendingEvent->type == EventEntry::Type::KEY) {KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);if (pendingKey.keyCode == keyEntry.keyCode &&pendingKey.interceptKeyResult ==KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;pendingKey.interceptKeyWakeupTime = 0;needWake = true;}}break;}case EventEntry::Type::MOTION: {LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,"Unexpected untrusted event.");if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {mNextUnblockedEvent = mInboundQueue.back();needWake = true;}break;}case EventEntry::Type::FOCUS: {LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");break;}case EventEntry::Type::TOUCH_MODE_CHANGED:case EventEntry::Type::CONFIGURATION_CHANGED:case EventEntry::Type::DEVICE_RESET:case EventEntry::Type::SENSOR:case EventEntry::Type::POINTER_CAPTURE_CHANGED:case EventEntry::Type::DRAG: {// nothing to dobreak;}}return needWake;
}

通过这两个函数可以看出,到达InputDispatcher的Motion事件被保存在MotionEntry类中,然后排在mInboundQueue列表的队尾,这个mInboundQueue就是InputDispatcher的派发队列。MotionEntry是EventEntry的一个子类,保存了Motion事件的信息。Key事件也有一个KeyEntry与之对应。EventEntry是输入事件在InputDispatcher中的存在形式。另外,由于InputDispatcher在没有事件可以派发时(mInboundQueue为空),将会进入休眠状态,因此在将事件放入派发队列时,需要将派发线程唤醒。

上述过程运行在inputReader线程中,至此inputReader的处理完成

相关文章:

Android14 InputManager-InputReader的处理

IMS启动时会调用InputReader.start()方法 InputReader.cpp status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });…...

web前端安全性——JSONP劫持

1、JSONP概念 JSONP(JSON with Padding)是JSON的一种“使用模式”&#xff0c;可用于解决主流浏览器的跨域数据访问的问题。由于同源策略&#xff0c;协议IP端口有任意不同都会导致请求跨域&#xff0c;而HTML的script元素是一个例外。利用script元素的这个开放策略&#xff0…...

从零开始学HCIA之广域网技术03

1、LCP中包含的报文类型 &#xff08;1&#xff09;Configure-Request&#xff08;配置请求&#xff09;&#xff0c;链路层协商过程中发送的第一个报文&#xff0c;该报文表明点对点双方开始进行链路层参数的协商。 &#xff08;2&#xff09; Configure-Ack&#xff08;配置…...

AI推介-大语言模型LLMs论文速览(arXiv方向):2024.01.01-2024.01.10

1.Pre-trained Large Language Models for Financial Sentiment Analysis 标题:用于金融情感分析的预训练大型语言模型 author:Wei Luo, Dihong Gong date Time:2024-01-10 paper pdf:http://arxiv.org/pdf/2401.05215v1 摘要&#xff1a; 金融情感分析是指将金融文本内容划分…...

Redis降低内存占用(二)分片结构

一、分区方法&#xff1a; 分片&#xff0c;也称为分区。Redis提供了多种分区实现方案: 1、哈希分区 2、区间分区 3、一致性哈希分区 4、虚拟分区 5、LUA脚本实现分片 二、...

vue大文件读取部分内容,避免重复加载大文件,造成流量浪费

使用场景&#xff1a;项目点云地图是pcd文件&#xff0c;但是文件可能上百兆&#xff0c;我需要获取到文件中的版本信息&#xff0c;跟本地的缓存文件做比较&#xff0c;如果不一致&#xff0c;才会加载整个文件。从而节省流量。 避免重复加载整个“.pcd文件&#xff0c;以最大…...

5G网络RedCap

RedCap&#xff1a;RedCap&#xff08;Reduced Capability&#xff09;&#xff0c;即“降低能力”。它是3GPP在5G R17阶段&#xff0c;针对速率、时延要求不高的5G应用场景&#xff0c;专门推出的一种新技术标准协议&#xff0c;旨在全面提升5G网络质量和覆盖率&#xff0c;也…...

vue+springboot登录与注册功能的实现

①首先写一个登录页面 <template> <div style"background-color: #42b983;display: flex;align-items: center;justify-content: center;height: 100vh"><div style"background-color: white;display: flex;width: 50%;height: 50%;overflow: h…...

数据结构D3作业

1. 2. 按位插入 void insert_pos(seq_p L,datatype num,int pos) { if(LNULL) { printf("入参为空&#xff0c;请检查\n"); return; } if(seq_full(L)1) { printf("表已满&#xff0c;不能插入\n"); …...

Spring框架@Autowired注解进行字段时,使用父类类型接收子类变量,可以注入成功吗?(@Autowired源码跟踪)

一、 前言 平常我们在使用spring框架开发项目过程中&#xff0c;会使用Autowired注解进行属性依赖注入&#xff0c;一般我们都是声明接口类型来接收接口实现变量&#xff0c;那么使用父类类型接收子类变量&#xff0c;可以注入成功吗&#xff1f;答案是肯定可以的&#xff01;…...

【springblade】springblade(bladeX) 数据权限失效原因分析

文章目录 数据权限接口权限 前言&#xff1a;最近博主在按照bladeX官方文档 配置数据权限 结果发现失效了&#xff0c;网上搜了一下没找到合适的答案&#xff0c;本着求人不如求己的精神&#xff0c;自己调试了一下发现了问题所在&#xff0c;也大致看了一下bladeX的权限逻辑。…...

单例模式的几种实现方式

在Java中&#xff0c;实现单例模式主要有几种方式&#xff1a;懒汉式、饿汉式、双重检查锁定、静态内部类和枚举。每种方式都有其特点和适用场景。 1. 饿汉式&#xff08;线程安全&#xff09; 饿汉式是最简单的一种实现方式&#xff0c;通过静态初始化实例&#xff0c;保证了…...

鸿蒙OS运行报错 ‘ToDoListItem({ item })‘ does not meet UI component syntax.

在学习harmonyOS时&#xff0c;原本是好好运行的。但是突然报错 ToDoListItem({ item }) does not meet UI component syntax. 一脸懵逼&#xff0c;以为是自己语法问题检查了半天也没问题。 网上搜索了一下&#xff0c;说把多余的js\map文件删除就行 才发现我的 鸿蒙的开…...

React18源码: reconciler执行流程

reconciler执行流程 1 &#xff09;概述 此处先归纳一下react-reconciler包的主要作用&#xff0c;将主要功能分为4个方面&#xff1a; 输入&#xff1a;暴露api函数&#xff08;如&#xff1a;scheduleUpdateOnFiber&#xff09;, 供给其他包&#xff08;如react包&#xff0…...

mapbox面图层标注

mapbox并没有一个属性类似于’text-field’的symbol图层的直接可以标注的办法&#xff0c;这里笔者提供两种其他的面图层标注的办法用来大家参考 效果图 方案一 把面图层当做点图层直接展示 在mapbox里面&#xff0c;面图层是可以直接渲染成线图层和点图层的&#xff0c;这里…...

MySQL|MySQL基础(求知讲堂-学习笔记【详】)

MySQL基础 目录 MySQL基础一、 MySQL的结构二、 管理数据库1&#xff09;查询所有的数据库2&#xff09;创建数据库3&#xff09;修改数据库的字符编码4&#xff09;删除数据库5&#xff09;切换操作的数据库 三、表的概念四、字段的数据类型4.1 整型4.2 浮点型(float和double)…...

10.docker exec -it /bin/bash报错解决、sh与bash区别

报错 进入容器时&#xff0c;报如下错误 dockeruserdell-PowerEdge-R740:~$ docker exec -it daf2 /bin/bash OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown…...

查询数据库的编码集Oracle,MySQL

1、查询数据库的编码集Oracle,MySQL 1.1、oracle select * from v$nls_parameters where parameterNLS_CHARACTERSET; 查询版本&#xff1a;SELECT * FROM v$version 2、MySQL编码集 SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SC…...

电商数据采集+跨境电商|API电商数据采集接口洞悉数字新零售发展

随着全球经济一体化和电子商务的快速发展&#xff0c;网络购物的需求日益增加。不断涌现的电商企业使得行业的竞争情况愈演愈烈。在这种情况下&#xff0c;企业不仅要加大经营力度&#xff0c;还要在自己的基础设施和技术上持续投入&#xff0c;才能更好的适应市场和消费习惯。…...

linux之用户和用户组

文章目录 一、简介1.1 用户1.2 用户组1.3 UID和GID1.4 用户账户分类 二、用户2.1 添加新的用户账号&#xff1a;useradd2.2 删除账号&#xff1a;userdel2.3 修改账号&#xff1a;usermod(modmodify)2.4 用户口令的管理:passwd2.5 切换用户&#xff1a;su 三、用户组3.1 增加一…...

人工智能深度学习

目录 人工智能 深度学习 机器学习 神经网络 机器学习的范围 模式识别 数据挖掘 统计学习 计算机视觉 语音识别 自然语言处理 机器学习的方法 回归算法 神经网络 SVM&#xff08;支持向量机&#xff09; 聚类算法 降维算法 推荐算法 其他 机器学习的分类 机器…...

python reshape 和 transpose的区别

reshape() 和 transpose() 是用于改变数组或张量形状的两种不同方法&#xff0c; 它们的主要区别在于如何重新排列元素以及是否可以改变轴的顺序。 1 reshape() reshape() 函数用于改变数组或张量的形状&#xff0c;但是不改变元素的排列顺序。它只是简单地将数组的维度重新…...

音视频技术-网络视频会议“回声”的消除

目录 一、“回音”的成因原理 二、解决思路 三、解决方案 1、方案一 2...

有哪些令人惊讶的心理学效应

大家可以想象一个场景&#xff1a; 如果一次考试&#xff0c;你考了95分&#xff0c;比上次还进步了10分&#xff0c;你会感到高兴吗&#xff1f; 听起来很牛逼啊&#xff0c;值得干杯庆祝&#xff0c;好好开心几天了。 这时&#xff0c;你看到同桌这次居然是一百分&#xf…...

二叉树基础知识总结

目录 二叉树基础知识 概念 : 根节点的五个形态 : 特殊的二叉树 满二叉树 : 完全二叉树 : 二叉搜索树 : 平衡二叉搜索树 : 二叉树的性质 : 二叉树的存储结构 二叉树的顺序存储结构 二叉树的链式存储结构 二叉树的遍历方式 : 基础概念 前中后遍历 层序遍历 :…...

IDEA2023.3.4开启SpringBoot项目的热部署【简单明了4步操作】

添加devtools依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional> </dependency>IDEA开启自动编译 …...

QT中调用python

一.概述 1.Python功能强大&#xff0c;很多Qt或者c/c开发不方便的功能可以由Python编码开发&#xff0c;尤其是一些算法库的应用上&#xff0c;然后Qt调用Python。 2.在Qt调用Python的过程中&#xff0c;必须要安装python环境&#xff0c;并且Qt Creator中编译器与Python的版…...

Sora基础知识学习

...

开源博客项目Blog .NET Core源码学习(9:Autofac使用浅析)

开源博客项目Blog使用Autofac注册并管理组件和服务&#xff0c;Autofac是面向.net 的开源IOC容器&#xff0c;支持通过接口、实例、程序集等方式注册组件和服务&#xff0c;同时支持属性注入、方法注入等注入方式。本文学习并记录Blog项目中Autofac的使用方式。   整个Blog解…...

Go语言中的TLS加密:深入crypto/tls库的实战指南

Go语言中的TLS加密&#xff1a;深入crypto/tls库的实战指南 引言crypto/tls库的核心组件TLS配置&#xff1a;tls.Config证书加载与管理TLS握手过程及其实现 构建安全的服务端创建TLS加密的HTTP服务器配置TLS属性常见的安全设置和最佳实践 开发TLS客户端应用编写使用TLS的客户端…...