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

frameworks 之 触摸事件窗口查找

frameworks 之 触摸事件窗口查找

  • 1. 初始化数据
  • 2. 查找窗口
  • 3. 分屏处理
  • 4. 检查对应的权限
  • 5.是否需要将事件传递给壁纸界面
  • 6. 成功处理

触摸流程中最重要的流程之一就是查找需要传递输入事件的窗口,并将触摸事件传递下去。
涉及到的类如下

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

上文讲到 进行 dispatchMotionLocked 后,会通过 findTouchedWindowTargetsLocked 进行窗口查找

1. 初始化数据

  1. 会将当前数据复制到 tempTouchState 数据中。
  2. 获取当前是否分屏保存到 isSplit
  3. 判断是否触摸是否切换了设备,保存在 switchedDevice 变量
  4. 判断是否第一个手指按下 保存在 newGesture 变量
InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {...const TouchState* oldState = nullptr;TouchState tempTouchState;std::unordered_map<int32_t, TouchState>::iterator oldStateIt =mTouchStatesByDisplay.find(displayId);if (oldStateIt != mTouchStatesByDisplay.end()) {oldState = &(oldStateIt->second);tempTouchState.copyFrom(*oldState);}// 代表是否支持分屏bool isSplit = tempTouchState.split;// 是否切换输入设备 比如注入事件 然后手指按下bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&(tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||tempTouchState.displayId != displayId);bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);// 是否第一个手指按下bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);}

2. 查找窗口

通过 findTouchedWindowAtLocked 获取对应的 WindowInfoHandle 。、

  1. 根据 displayId 通过 getWindowHandlesLocked 方法获取对应的 WindowInfoHandle 集合,该集合通过 Surfaceflings*刷新的时候 添加
  2. 获取后进行遍历,首先判断 displayId是否一样,一样才进入判断。
  3. 判断window 是否可见
  4. 接着判断 window 是否没带 NOT_TOUCHABLE 的 flag,如果带了则过滤了该窗口。
  5. 判断窗口是否聚焦 并且 不带 NOT_TOUCH_MODAL (表示窗口聚焦的时候,设置了该 flag 不能将事件传递给外部窗口,反之没设置则可以),如果可以传递 或在触摸的 坐标在 窗口内则是该窗口
  6. 最后还需判断该窗口是否 投屏窗口 如果是的话 则需要递归传入投屏的displayId 进行查找在返回,否则返回该窗口。
  7. 还有判断是否带有 WATCH_OUTSIDE_TOUCH 的 flag,如果为支持外部触碰则也通过 addOrUpdateWindow 添加到 touchState
// 查找对应的可触摸window
sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,int32_t y, TouchState* touchState,bool addOutsideTargets,bool addPortalWindows,bool ignoreDragWindow) {if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {LOG_ALWAYS_FATAL("Must provide a valid touch state if adding portal windows or outside targets");}// Traverse windows from front to back to find touched window.// 返回可见的window数组const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {continue;}const WindowInfo* windowInfo = windowHandle->getInfo();// 判断屏幕是否一样if (windowInfo->displayId == displayId) {auto flags = windowInfo->flags;// 是否可见if (windowInfo->visible) {// window的flag是否没设置不可触摸if (!flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {// FLAG_NOT_TOUCH_MODAL 非模态对话框 表示即使window是处于上面的默认状态下,设置了该Flag,// 新window范围外的view也是可以响应touch事件。是模态的话 该范围外的 不可响应对应的事件bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&!flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);// 触摸是否包含在对应的window内,或者窗口是虚拟屏if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {int32_t portalToDisplayId = windowInfo->portalToDisplayId;if (portalToDisplayId != ADISPLAY_ID_NONE &&portalToDisplayId != displayId) {if (addPortalWindows) {// For the monitoring channels of the display.touchState->addPortalWindow(windowHandle);}// 不相等则调用方法查找,但是查找的是对应的displayIdreturn findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,addOutsideTargets, addPortalWindows);}// Found window.return windowHandle;}}// 是否外部可点击if (addOutsideTargets && flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {touchState->addOrUpdateWindow(windowHandle,InputTarget::FLAG_DISPATCH_AS_OUTSIDE,BitSet32(0));}}}}return nullptr;
}

3. 分屏处理

  1. 会判断是否第一次触摸 或者 表示上一次触摸的窗口支持分屏,并且这次事件是按下事件
  2. 通过 findTouchedWindowAtLocked 获取对应的 WindowInfoHandle 上面已经讲解
  3. 如果查找不为空,判断窗口是否允许分屏,并赋值给 isSplit ,如果 是鼠标事件一定不支持。
  4. 因为上一次的支持分屏,但是新的窗口不支持分屏,将查找到的windowHandle置为空,忽略此次窗口
  5. 接下来都是判断一些窗口连接是否在,是否需要抛弃此次事件等判断,将 windowHandle 置为空
  6. 如果新窗口,并且手指监听也为空,则进入失败代码逻辑
  7. 如果窗口不为空,则添加 各种 flag 如 FLAG_FOREGROUNDFLAG_DISPATCH_AS_IS,最后 也是通过 tempTouchState.addOrUpdateWindow 添加到 tempTouchState。
	// 第一个条件 newGesture 表示第一个手指按下// 后面一个条件,表示当前窗口支持 split motion,并且此时有另外一个手指按下if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {/* Case 1: New splittable pointer going down, or need target for hover or scroll. */int32_t x;int32_t y;int32_t pointerIndex = getMotionEventActionPointerIndex(action);// Always dispatch mouse events to cursor position.// 是否鼠标对xy进行修正if (isFromMouse) {x = int32_t(entry.xCursorPosition);y = int32_t(entry.yCursorPosition);} else {x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));}// 这里检测是否是第一个手指按下bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;newTouchedWindowHandle =findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,isDown /*addOutsideTargets*/, true /*addPortalWindows*/);// Figure out whether splitting will be allowed for this window.// 判断新窗口是否允许分屏操作if (newTouchedWindowHandle != nullptr &&newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {// New window supports splitting, but we should never split mouse events.// 如果是鼠标事件一定不支持分屏isSplit = !isFromMouse;} else if (isSplit) {// New window does not support splitting but we have already split events.// Ignore the new window.// 因为上一次的支持分屏,但是新的窗口不支持分屏,将查找到的windowHandle置为空,忽略此次窗口newTouchedWindowHandle = nullptr;}// Handle the case where we did not find a window.if (newTouchedWindowHandle == nullptr) {// Try to assign the pointer to the first foreground window we find, if there is one.newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();}if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {ALOGI("Not sending touch event to %s because it is paused",newTouchedWindowHandle->getName().c_str());newTouchedWindowHandle = nullptr;}// Ensure the window has a connection and the connection is responsiveif (newTouchedWindowHandle != nullptr) {const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle);if (!isResponsive) {ALOGW("%s will not receive the new gesture at %" PRIu64,newTouchedWindowHandle->getName().c_str(), entry.eventTime);newTouchedWindowHandle = nullptr;}}// Drop events that can't be trusted due to occlusionif (newTouchedWindowHandle != nullptr &&mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {TouchOcclusionInfo occlusionInfo =computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);if (!isTouchTrustedLocked(occlusionInfo)) {if (DEBUG_TOUCH_OCCLUSION) {ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);for (const auto& log : occlusionInfo.debugInfo) {ALOGD("%s", log.c_str());}}onUntrustedTouchLocked(occlusionInfo.obscuringPackage);if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {ALOGW("Dropping untrusted touch event due to %s/%d",occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);newTouchedWindowHandle = nullptr;}}}// Drop touch events if requested by input featureif (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {newTouchedWindowHandle = nullptr;}const std::vector<TouchedMonitor> newGestureMonitors = isDown? selectResponsiveMonitorsLocked(findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)): tempTouchState.gestureMonitors;// 如果新窗口,并且手指监听也为空,则进入失败代码逻辑if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {ALOGI("Dropping event because there is no touchable window or gesture monitor at ""(%d, %d) in display %" PRId32 ".",x, y, displayId);injectionResult = InputEventInjectionResult::FAILED;goto Failed;}if (newTouchedWindowHandle != nullptr) {// Set target flags.// 标记为前段可触摸int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;// 是否可分离if (isSplit) {targetFlags |= InputTarget::FLAG_SPLIT;}// 是不是被人覆盖if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;} else if (isWindowObscuredLocked(newTouchedWindowHandle)) {targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;}// Update hover state.if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {newHoverWindowHandle = nullptr;} else if (isHoverAction) {newHoverWindowHandle = newTouchedWindowHandle;}// Update the temporary touch state.BitSet32 pointerIds;if (isSplit) {uint32_t pointerId = entry.pointerProperties[pointerIndex].id;pointerIds.markBit(pointerId);}// 添加到touchStatetempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);} else if (tempTouchState.windows.empty()) {// If no window is touched, set split to true. This will allow the next pointer down to// be delivered to a new window which supports split touch.tempTouchState.split = true;}if (isDown) {tempTouchState.addGestureMonitors(newGestureMonitors);}}

else 部分 是判断 move 事件 的查找。主要判断是否允许事件滑动到其他窗口 如 toast

4. 检查对应的权限

  1. 开始和i遍历里面的window 判断 flag 是否在前台
  2. 通过 checkInjectionPermission 判断权限,非系统应用,只能事件注入在自己应用。
  3. 遍历完如果又没前台窗口,全局手势也为空,则跳转失败处理
// Check permission to inject into all touched foreground windows and ensure there// is at least one touched foreground window.{bool haveForegroundWindow = false;for (const TouchedWindow& touchedWindow : tempTouchState.windows) {if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {// 是否前台haveForegroundWindow = true;// 检查权限 注入事件只能在自己应用 除非其他系统权限if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {injectionResult = InputEventInjectionResult::PERMISSION_DENIED;injectionPermission = INJECTION_PERMISSION_DENIED;goto Failed;}}}// 如果又没前台窗口,全局手势也为空,则跳转失败处理bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();if (!haveForegroundWindow && !hasGestureMonitor) {ALOGI("Dropping event because there is no touched foreground window in display ""%" PRId32 " or gesture monitor to receive it.",displayId);injectionResult = InputEventInjectionResult::FAILED;goto Failed;}// Permission granted to injection into all touched foreground windows.injectionPermission = INJECTION_PERMISSION_GRANTED;}

5.是否需要将事件传递给壁纸界面

判断当前前台的窗口是否带 hasWallpaper 属性,如果是的话 则遍历对应 windowHandles 集合,如果对应的 displayId一样并且 type为 WindowInfo::Type::WALLPAPER。则添加进行。这就是为啥触摸桌面,壁纸也会动。

// 是否带壁纸属性,如果是则找到壁纸的window 事件传递给他.如桌面if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {sp<WindowInfoHandle> foregroundWindowHandle =tempTouchState.getFirstForegroundWindowHandle();if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {const std::vector<sp<WindowInfoHandle>>& windowHandles =getWindowHandlesLocked(displayId);for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {const WindowInfo* info = windowHandle->getInfo();if (info->displayId == displayId &&windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {tempTouchState.addOrUpdateWindow(windowHandle,InputTarget::FLAG_WINDOW_IS_OBSCURED |InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED |InputTarget::FLAG_DISPATCH_AS_IS,BitSet32(0));}}}}

6. 成功处理

如果都ok的话 则分别将对应的window添加到 inputTager数组 和 全局 monitoringTarget 数组 返回结果

// Success!  Output targets.injectionResult = InputEventInjectionResult::SUCCEEDED;for (const TouchedWindow& touchedWindow : tempTouchState.windows) {// 添加到inputTarget里面addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,touchedWindow.pointerIds, inputTargets);}for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,touchedMonitor.yOffset, inputTargets);}

相关文章:

frameworks 之 触摸事件窗口查找

frameworks 之 触摸事件窗口查找 1. 初始化数据2. 查找窗口3. 分屏处理4. 检查对应的权限5.是否需要将事件传递给壁纸界面6. 成功处理 触摸流程中最重要的流程之一就是查找需要传递输入事件的窗口&#xff0c;并将触摸事件传递下去。 涉及到的类如下 frameworks/native/service…...

memset的用法

memset 是 C 语言标准库中的一个函数&#xff0c;用于将一块内存区域设置为特定的值。它的原型如下&#xff1a; c void *memset(void *s, int c, size_t n); - s 参数是要被填充的内存块的起始地址。 - c 参数是要填充的值。这个值会被转换为无符号字符&#xff0c;然后用来…...

阿里云国际站DDoS高防增值服务怎么样?

利用国外服务器建站的话&#xff0c;选择就具有多样性了&#xff0c;相较于我们常见的阿里云和腾讯云&#xff0c;国外的大厂商还有谷歌云&#xff0c;微软云&#xff0c;亚马逊云等&#xff0c;但是较之这些&#xff0c;同等产品进行比较的话&#xff0c;阿里云可以说当之无愧…...

open-cd中的changerformer网络结构分析

open-cd 目录 open-cd1.安装2.源码结构分析主干网络1.1 主干网络类2.neck2.Decoder3.测试模型6. changer主干网络 总结 该开源库基于&#xff1a; mmcv mmseg mmdet mmengine 1.安装 在安装过程中遇到的问题&#xff1a; 1.pytorch版本问题&#xff0c;open-cd采用的mmcv版本比…...

太速科技-426-基于XC7Z100+TMS320C6678的图像处理板卡

基于XC7Z100TMS320C6678的图像处理板卡 一、板卡概述 板卡基于独立的结构&#xff0c;实现ZYNQ XC7Z100DSP TMS320C6678的多路图像输入输出接口的综合图像处理&#xff0c;包含1路Camera link输入输出、1路HD-SDI输入输出、1路复合视频输入输出、2路光纤等视频接口&#xff0c;…...

asp.net Core 自定义中间件

内联中间件 中间件转移到类中 推荐中间件通过IApplicationBuilder 公开中间件 使用扩展方法 调用中间件 含有依赖项的 》》》中间件 参考资料...

掌握 C# 设计模式:从基础到依赖注入

设计模式是一种可以在开发中重复使用的解决方案&#xff0c;能够提高代码的可维护性、扩展性和复用性。C# 中常见的设计模式包括单例模式、工厂模式、观察者模式、策略模式等。本文将介绍这些常见的设计模式&#xff0c;并探讨 SOLID 原则和依赖注入&#xff08;Dependency Inj…...

根据json转HttpClient脚本

String json “{\n” " “paths”: {\n" " “/dev-api/system/subjectResult/exportUserList”: {\n" " “post”: {\n" " “tags”: [\n" " “bd-subject-result-controller”\n" " ],\n" " “summ…...

如何将LiDAR坐标系下的3D点投影到相机2D图像上

将激光雷达点云投影到相机图像上做数据层的前融合&#xff0c;或者把激光雷达坐标系下标注的物体点云的3d bbox投影到相机图像上画出来&#xff0c;都需要做点云3D点坐标到图像像素坐标的转换计算&#xff0c;也就是LiDAR 3D坐标转像素坐标。 看了网上一些文章都存在有错误或者…...

JAVA就业笔记6——第二阶段(3)

课程须知 A类知识&#xff1a;工作和面试常用&#xff0c;代码必须要手敲&#xff0c;需要掌握。 B类知识&#xff1a;面试会问道&#xff0c;工作不常用&#xff0c;代码不需要手敲&#xff0c;理解能正确表达即可。 C类知识&#xff1a;工作和面试不常用&#xff0c;代码不…...

02.04、分割链表

02.04、[中等] 分割链表 1、题目描述 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 2、解题思路 本题要求将链表分隔…...

Excel 中根据患者的就诊时间标记病例为“初诊”或“复诊”

1. 假设&#xff1a; 患者表&#xff1a;包含患者的基本信息&#xff0c;如患者 ID 和患者姓名。 病例表&#xff1a;包含病例信息&#xff0c;如患者 ID、就诊时间和就诊状态。 2. 操作步骤&#xff1a; 合并数据&#xff1a; 确保病例表中有一列包含患者 ID&#xff0c;以…...

遇到“mfc100u.dll丢失”的系统错误要怎么处理?科学修复mfc100u.dll

遇到“mfc100u.dll丢失”的系统错误会非常麻烦&#xff0c;因为mfc100u.dll是Microsoft Visual C 2010 Redistributable Package的重要部分&#xff0c;许多应用程序和游戏在运行时都需要调用这个文件。如果这个文件缺失&#xff0c;可能会导致相关软件或游戏启动失败。面对这种…...

[Linux] 逐层深入理解文件系统 (1)—— 进程操作文件

标题&#xff1a;[Linux] 文件系统 &#xff08;1&#xff09;—— 进程操作文件 个人主页水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、进程与打开的文件 二、文件的系统调用与库函数的关系 1.系统调用open() 三、内存中的文件描述符表 四、缓冲区…...

RT-Thread 互斥量的概念

目录 概述 1 互斥量定义 1.1 概念介绍 1.2 线程优先级翻转问题 2 互斥量管理 2.1 结构体定义 2.2 函数接口介绍 2.2.1 rt_mutex_create函数 2.2.2 rt_mutex_delete 函数 2.2.3 初始化和脱离互斥量 概述 本文主要介绍互斥量的概念&#xff0c;实现原理。还介绍RT-Thre…...

6.计算机网络_UDP

UDP的主要特点&#xff1a; 无连接&#xff0c;发送数据之前不需要建立连接。不保证可靠交付。面向报文。应用层给UDP报文后&#xff0c;UDP并不会抽象为一个一个的字节&#xff0c;而是整个报文一起发送。没有拥塞控制。网络拥堵时&#xff0c;发送端并不会降低发送速率。可以…...

Windows应急响蓝安服面试

Windows应急响应 蓝队溯源流程 学习Windows应急首先要站在攻击者的角度去学习一些权限维持和权限提升的方法.,文章中的方法其实和内网攻防笔记有类似l红队教你怎么利用 蓝队教你怎么排查 攻防一体,应急响应排查这些项目就可以 端口/服务/进程/后门文件都是为了权限维持,得到s…...

PCL 点云配准-4PCS算法(粗配准)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 加载点云数据 2.1.2 执行4PCS粗配准 2.1.3 可视化源点云、目标点云和配准结果 2.2完整代码 三、实现效果 3.1原始点云 3.2配准后点云 PCL点云算法汇总及实战案例汇总的目录地址链接…...

12、论文阅读:利用生成对抗网络实现无监督深度图像增强

Towards Unsupervised Deep Image Enhancement With Generative Adversarial Network 摘要介绍相关工作传统图像增强基于学习的图像增强 论文中提出的方法动机和目标网络架构损失函数1) 质量损失2) 保真损失3&#xff09;身份损失4&#xff09;Total Loss 实验 摘要 提高图像的…...

Axure重要元件三——中继器表单制作

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 本节课&#xff1a;中继器表单制作 课程内容&#xff1a;利用中继器制作表单 应用场景&#xff1a;台账、表单 案例展示&#xff1a; 步骤一&#xff1a;建立一个背景区…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...