frameworks 之 触摸事件窗口查找
frameworks 之 触摸事件窗口查找
- 1. 初始化数据
- 2. 查找窗口
- 3. 分屏处理
- 4. 检查对应的权限
- 5.是否需要将事件传递给壁纸界面
- 6. 成功处理
触摸流程中最重要的流程之一就是查找需要传递输入事件的窗口,并将触摸事件传递下去。
涉及到的类如下
- frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
上文讲到 进行 dispatchMotionLocked 后,会通过 findTouchedWindowTargetsLocked 进行窗口查找
1. 初始化数据
- 会将当前数据复制到 tempTouchState 数据中。
- 获取当前是否分屏保存到 isSplit
- 判断是否触摸是否切换了设备,保存在 switchedDevice 变量
- 判断是否第一个手指按下 保存在 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 。、
- 根据 displayId 通过 getWindowHandlesLocked 方法获取对应的 WindowInfoHandle 集合,该集合通过 Surfaceflings*刷新的时候 添加。
- 获取后进行遍历,首先判断 displayId是否一样,一样才进入判断。
- 判断window 是否可见
- 接着判断 window 是否没带 NOT_TOUCHABLE 的 flag,如果带了则过滤了该窗口。
- 判断窗口是否聚焦 并且 不带 NOT_TOUCH_MODAL (表示窗口聚焦的时候,设置了该 flag 不能将事件传递给外部窗口,反之没设置则可以),如果可以传递 或在触摸的 坐标在 窗口内则是该窗口
- 最后还需判断该窗口是否 投屏窗口 如果是的话 则需要递归传入投屏的displayId 进行查找在返回,否则返回该窗口。
- 还有判断是否带有 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. 分屏处理
- 会判断是否第一次触摸 或者 表示上一次触摸的窗口支持分屏,并且这次事件是按下事件
- 通过 findTouchedWindowAtLocked 获取对应的 WindowInfoHandle 上面已经讲解
- 如果查找不为空,判断窗口是否允许分屏,并赋值给 isSplit ,如果 是鼠标事件一定不支持。
- 因为上一次的支持分屏,但是新的窗口不支持分屏,将查找到的windowHandle置为空,忽略此次窗口
- 接下来都是判断一些窗口连接是否在,是否需要抛弃此次事件等判断,将 windowHandle 置为空
- 如果新窗口,并且手指监听也为空,则进入失败代码逻辑
- 如果窗口不为空,则添加 各种 flag 如 FLAG_FOREGROUND,FLAG_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. 检查对应的权限
- 开始和i遍历里面的window 判断 flag 是否在前台
- 通过 checkInjectionPermission 判断权限,非系统应用,只能事件注入在自己应用。
- 遍历完如果又没前台窗口,全局手势也为空,则跳转失败处理
// 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. 成功处理 触摸流程中最重要的流程之一就是查找需要传递输入事件的窗口,并将触摸事件传递下去。 涉及到的类如下 frameworks/native/service…...
memset的用法
memset 是 C 语言标准库中的一个函数,用于将一块内存区域设置为特定的值。它的原型如下: c void *memset(void *s, int c, size_t n); - s 参数是要被填充的内存块的起始地址。 - c 参数是要填充的值。这个值会被转换为无符号字符,然后用来…...
阿里云国际站DDoS高防增值服务怎么样?
利用国外服务器建站的话,选择就具有多样性了,相较于我们常见的阿里云和腾讯云,国外的大厂商还有谷歌云,微软云,亚马逊云等,但是较之这些,同等产品进行比较的话,阿里云可以说当之无愧…...

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

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

asp.net Core 自定义中间件
内联中间件 中间件转移到类中 推荐中间件通过IApplicationBuilder 公开中间件 使用扩展方法 调用中间件 含有依赖项的 》》》中间件 参考资料...
掌握 C# 设计模式:从基础到依赖注入
设计模式是一种可以在开发中重复使用的解决方案,能够提高代码的可维护性、扩展性和复用性。C# 中常见的设计模式包括单例模式、工厂模式、观察者模式、策略模式等。本文将介绍这些常见的设计模式,并探讨 SOLID 原则和依赖注入(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图像上
将激光雷达点云投影到相机图像上做数据层的前融合,或者把激光雷达坐标系下标注的物体点云的3d bbox投影到相机图像上画出来,都需要做点云3D点坐标到图像像素坐标的转换计算,也就是LiDAR 3D坐标转像素坐标。 看了网上一些文章都存在有错误或者…...

JAVA就业笔记6——第二阶段(3)
课程须知 A类知识:工作和面试常用,代码必须要手敲,需要掌握。 B类知识:面试会问道,工作不常用,代码不需要手敲,理解能正确表达即可。 C类知识:工作和面试不常用,代码不…...
02.04、分割链表
02.04、[中等] 分割链表 1、题目描述 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 2、解题思路 本题要求将链表分隔…...
Excel 中根据患者的就诊时间标记病例为“初诊”或“复诊”
1. 假设: 患者表:包含患者的基本信息,如患者 ID 和患者姓名。 病例表:包含病例信息,如患者 ID、就诊时间和就诊状态。 2. 操作步骤: 合并数据: 确保病例表中有一列包含患者 ID,以…...

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

[Linux] 逐层深入理解文件系统 (1)—— 进程操作文件
标题:[Linux] 文件系统 (1)—— 进程操作文件 个人主页水墨不写bug (图片来源于网络) 目录 一、进程与打开的文件 二、文件的系统调用与库函数的关系 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 初始化和脱离互斥量 概述 本文主要介绍互斥量的概念,实现原理。还介绍RT-Thre…...

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

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)身份损失4)Total Loss 实验 摘要 提高图像的…...

Axure重要元件三——中继器表单制作
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 本节课:中继器表单制作 课程内容:利用中继器制作表单 应用场景:台账、表单 案例展示: 步骤一:建立一个背景区…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...