启用线程执行获取屏幕所有文本,遇见制定内容点击
第 1 条补充
· 1 个月前
如何快速点击,指定文本右侧的图片按钮
第 2 条补充
· 1 个月前
在后台执行脚本中,脚本运行长时间后不执行了,如何用代码避免此类情况
回答
要实现在子线程中监控屏幕并点击特定文本的功能,可以使用以下脚本:
// 文档地址 https://docs.hamibot.com/
threads.start(function () {
const check = () => {
// 查找所有文本控件
let textViews = className("TextView").find();
// 遍历文本控件
for (let i = 0; i < textViews.length; i++) {
let textView = textViews[i];
let textContent = textView.text();
// 判断是否匹配目标文本(可修改为你的指定内容)
if (textContent === "指定内容") {
// 尝试点击目标文本
if (textView.click()) {
toastLog("成功点击目标文本: " + textContent);
return; // 点击成功后退出循环
}
}
}
// 每隔500ms检查一次
setTimeout(check, 500);
};
check();
});
// 主线程继续执行其他操作
toastLog("已启动后台文本监控线程");
关键点说明:
- 使用
className("TextView").find()
获取屏幕上的所有文本控件 - 通过
textView.text()
获取控件的文本内容 - 通过
textView.click()
对匹配的文本执行点击操作 - 线程每隔500ms重新检查屏幕内容
- 使用
toastLog()
显示操作日志
适配建议:
- 若需要模糊匹配,可将
textContent === "指定内容"
改为:textContent.includes("目标关键词") // 包含关键词匹配 // 或 textContent.match(/正则表达式/) // 正则表达式匹配
- 如需点击特定位置,可使用
click(x, y)
指定坐标 - 可通过
textView.bounds()
获取控件坐标范围
注意事项:
- 需开启无障碍服务和弹窗权限
- 脚本在主线程继续执行其他逻辑时,后台线程会持续监控
- 修改目标文本时请确保字符串准确性
- 复杂界面建议配合
desc()
等属性联合判断
如果需要处理动态加载内容,建议在检查逻辑中增加:
if (!textView.isSameLayout()) {
// 布局变化时重新扫描
}
补充回答 1
实现点击指定文本右侧图片按钮的方案如下,通过控件层级关系和坐标偏移两种方式实现:
// 方法一:基于控件层级关系(推荐)
function clickRightImageByText(text) {
// 查找文本控件
var textControl = text(text).findOne(2000);
if (!textControl) {
toastLog("未找到文本控件");
return false;
}
// 获取父容器
var parent = textControl.parent();
if (!parent) {
toastLog("未找到父容器");
return false;
}
// 遍历父容器子控件(假设图片是文本控件之后的兄弟节点)
for (var i = 0; i < parent.childCount(); i++) {
var child = parent.child(i);
if (child.className() === "ImageView" && child.bounds().left > textControl.bounds().right) {
if (child.click()) {
toastLog("成功点击右侧图片按钮");
return true;
}
}
}
return false;
}
// 示例调用
// clickRightImageByText("设置");
// 方法二:基于坐标偏移(备用方案)
function clickRightImageByText2(text) {
var target = text(text).findOne(2000);
if (!target) {
toastLog("未找到目标文本");
return false;
}
var bounds = target.bounds();
// 在文本右侧1/4高度处点击
var x = bounds.right + 5;
var y = bounds.top + (bounds.height() * 0.75);
// 执行点击
if (click(x, y)) {
toastLog("已执行坐标点击");
return true;
}
return false;
}
实现原理:
-
层级关系法:
- 通过
textControl.parent()
获取文本控件的父容器 - 遍历父容器的子控件,寻找满足两个条件的 ImageView:
- 类型为 ImageView(图片控件)
- 水平位置在文本控件右侧
- 通过
-
坐标偏移法:
- 获取文本控件的边界
bounds()
- 在其右侧 5px 偏移量位置,垂直方向 75% 高度处计算点击坐标
- 使用
click(x, y)
执行点击
- 获取文本控件的边界
使用建议:
- 优先使用层级关系法,稳定性更高
- 对于复杂布局建议:
- 在代码中添加
toastLog("控件结构: " + textControl.parent().toString())
调试布局 - 使用布局分析工具确认控件层级关系
- 在代码中添加
- 对于图标按钮可结合
desc()
属性判断:if (child.desc().includes("设置")) { /* 特定描述匹配 */ }
注意事项:
- 两种方法都建议添加超时检测(示例中使用 2000ms 超时)
- 坐标法需要根据实际布局调整偏移量(示例中的 5px)
- 确保脚本已获得无障碍服务权限
- 复杂界面建议组合使用
className
,desc
,id
等属性进行精确匹配
补充回答 2
为避免后台脚本长时间运行后停止执行,可采用以下结构化解决方案:
// 文档地址 https://docs.hamibot.com/
// 主线程监控机制
(function main() {
// 设置最大运行时间(单位:毫秒)
const MAX_RUNTIME = 60 * 60 * 1000; // 1小时
let startTime = new Date().getTime();
// 创建子线程执行任务
let workerThread = threads.start(function() {
try {
while (true) {
// 1. 核心任务逻辑
executeTask();
// 2. 运行时间检查
if (new Date().getTime() - startTime > MAX_RUNTIME) {
toastLog("达到最大运行时间,重启任务");
startTime = new Date().getTime();
}
// 3. 定期休眠释放资源
sleep(5000);
}
} catch (e) {
toastLog("子线程异常: " + e.message);
threads.shutDownAll(); // 异常时清理线程
}
});
// 定期心跳检测机制
setInterval(() => {
if (!workerThread.isAlive()) {
toastLog("检测到线程终止,重新启动");
workerThread = threads.start(function() {
// 重启任务逻辑
while(true) {
executeTask();
sleep(5000);
}
});
}
}, 10 * 60 * 1000); // 每10分钟检测一次
})();
// 独立任务模块
function executeTask() {
// 示例任务:查找特定文本并点击
try {
// 1. 设置执行超时
let timeout = 3000; // 任务阶段超时时间
let taskStart = Date.now();
// 2. 实际操作逻辑
if (text("目标文本").exists()) {
click("目标文本");
}
// 3. 阶段超时检测
if (Date.now() - taskStart > timeout) {
toastLog("任务阶段超时,进入下一轮");
}
} catch (e) {
toastLog("任务阶段异常: " + e.message);
}
}
关键防护机制解析:
-
时间分区机制(MAX_RUNTIME)
- 设置最大运行时间阈值(推荐1小时)
- 到达阈值后自动重置计时器
- 避免单次运行过长导致系统资源回收
-
线程健康监控
setInterval(() => { if (!workerThread.isAlive()) { // 线程异常恢复逻辑 } }, 10 * 60 * 1000);
- 每10分钟检测线程存活状态
- 异常终止时自动重启线程
- 确保后台任务持续可用
-
任务阶段超时控制
let timeout = 3000; let taskStart = Date.now(); // ...执行操作... if (Date.now() - taskStart > timeout) { // 强制进入下一轮循环 }
- 防止单个任务阶段无限执行
- 建议设置为实际操作所需时间的1.5-2倍
-
异常处理体系
try { // 核心逻辑 } catch (e) { // 统一异常捕获 threads.shutDownAll(); // 清理所有线程 } finally { // 资源释放逻辑 }
- 全局异常捕获防止线程崩溃
- 确保异常时正确清理资源
高级优化建议:
- 添加内存监控
setInterval(() => {
// 获取内存使用信息(单位MB)
let memory = parseFloat(java.lang.Runtime.getRuntime().totalMemory() / 1024 / 1024).toFixed(2);
if (memory > 50) { // 设置内存阈值
toastLog("内存使用过高(" + memory + "MB),重启脚本");
engines.execScriptFile(engine.cwd()); // 重启当前脚本
}
}, 5 * 60 * 1000);
- 实现优雅的线程退出:
// 主线程注册退出钩子
events.on("exit", () => {
// 执行清理操作
workerThread.int
ℹ️ 本回答由 AI 生成,内容仅供参考