当前位置: 首页 > 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;建立一个背景区…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...