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

DMAIC赋能智能家居:解锁未来生活新篇章!

从清晨自动拉开的窗帘&#xff0c;到夜晚自动调暗的灯光&#xff0c;每一处细节都透露着科技的温度与智慧的光芒。而在这场智能革命的浪潮中&#xff0c;DMAIC&#xff08;定义Define、测量Measure、分析Analyze、改进Improve、控制Control&#xff09;作为六西格玛管理的核心方…...

代码随想录算法训练营第二天| 209.长度最小的子数组 59.螺旋矩阵II 区间和 开发商购买土地

209. 长度最小的子数组 题目&#xff1a; 给定一个包含正整数的数组 nums 和一个正整数 target &#xff0c;找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0。 示例&#xff1a; 示例 1…...

mysql隐藏索引

1. 什么是隐藏索引&#xff1f; 在 MySQL 8 中&#xff0c;隐藏索引&#xff08;Invisible Indexes&#xff09;是指一种特殊类型的索引&#xff0c;它并不真正被删除&#xff0c;而是被标记为“不可见”。当索引被标记为不可见时&#xff0c;查询优化器在生成查询计划时将忽略…...

etcd入门到实战

概述&#xff1a;本文将介绍etcd特性、使用场景、基本原理以及Linux环境下的实战操作 入门 什么是etcd&#xff1f; etcd是一个分布式键值存储数据库 关键字解析&#xff1a; 键值存储&#xff1a;存储协议是 key—value 的形式&#xff0c;类似于redis分布式&#xff1a;…...

Build an Android project and get a `.apk` file on a Debian 11 command line

You can build an Android project and get a .apk file on a Debian 11 command line without using Android Studio. The process involves using the Android SDK command-line tools (sdkmanager, adb, and gradle). Here’s a step-by-step guide to building the ???…...

解读 Java 经典巨著《Effective Java》90条编程法则,第4条:通过私有构造器强化不可实例化的能力

文章目录 【前言】欢迎订阅【解读《Effective Java》】系列专栏java.lang.Math 类的设计经验总结 【前言】欢迎订阅【解读《Effective Java》】系列专栏 《Effective Java》是 Java 开发领域的经典著作&#xff0c;作者 Joshua Bloch 以丰富的经验和深入的知识&#xff0c;全面…...

Vivado HLS学习

视频链接: 6课&#xff1a;数据类型的转换_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1bt41187RW?spm_id_from333.788.videopod.episodes&vd_sourcea75d5585c5297210add71187236ec90b&p6 目录 1.数据类型的转换 2.自动类型转换 2.1隐式数据转换 2.2…...

一款AutoXJS现代化美观的日志模块AxpLogger

简介 Axp Logger是一款基于autox.js的现代化日志模块&#xff0c;具备窗口事件穿透、拖拽和缩放功能。 Axp Logger文档 特性现代化的UI设计支持点击穿透模式&#xff08;不影响脚本运行&#xff09;监听音量-键切换模式支持窗口操作模式窗口拖拽移动窗口自由缩放清空日志关闭日…...

成都睿明智科技有限公司共创抖音电商新篇章

在当今这个数字化浪潮汹涌的时代&#xff0c;抖音电商以其独特的魅力迅速崛起&#xff0c;成为众多商家竞相追逐的新蓝海。在这片充满机遇与挑战的领域中&#xff0c;成都睿明智科技有限公司凭借其专业的服务、创新的策略和敏锐的市场洞察力&#xff0c;成为了众多商家信赖的合…...

Spark的安装配置及集群搭建

Spark的本地安装配置&#xff1a; 我们用scala语言编写和操作spark&#xff0c;所以先要完成scala的环境配置 1、先完成Scala的环境搭建 下载Scala插件&#xff0c;创建一个Maven项目&#xff0c;导入Scala依赖和插件 scala依赖 <dependency><groupId>org.scal…...