写了一个悬浮窗代码,但是点击开始按钮以后,每次运行到gesture()以后,软件都会卡死,无响应,只能强制退出。在主线程里调用gesture则一点问题都没有。为什么会这样?悬浮窗回调函数对触摸操作有限制吗?以下为我的代码:
auto.waitFor();

var running = false;
var mission = null;

function OnlyGesture(){
gesture(500, [576, 1360], [555, 865]);
setTimeout(() => {
doTask()
}, 1000);
}

// 循环执行的任务
function doTask() {
if (!running) return;
setTimeout(() => {
toastLog("111");
}, 1000);
let delay = Math.ceil(Math.random() * 5) * 1000;
toastLog("下一次间隔为:" + delay.toString());
mission = setTimeout(() => {
doTask();
}, delay);
}

// 悬浮窗界面
let w = floaty.window(
<vertical padding="8" bg="#bababab4">
<text id="running">停止中</text>
<button id="start" text='开始' />
<button id="btn1" text='停止' />
<button id="btn3" text='退出' />
</vertical>
);

// 开始按钮的回调
w.start.click(() => {
if (running) {
toastLog("任务已经在执行!");
return;
}

running = true;
w.running.setText("运行中");
toastLog("任务启动成功");
setTimeout(OnlyGesture, 2000);

});

// 停止按钮
w.btn1.click(() => {
if (!running) {
toastLog("没有任务在运行!");
return;
}

running = false;
if (mission) {
    clearTimeout(mission);
    mission = null;
}
w.running.setText("停止中");
toastLog("任务已停止");

});

// 退出按钮
w.btn3.click(() => {
running = false;
if (mission) clearTimeout(mission);
w.close();
engines.myEngine().forceStop(); // 彻底退出脚本
});

// 悬浮窗基础设置
w.setAdjustEnabled(true);
w.setPosition(100, 100);
w.setSize(-2, -2);

// 脚本保活(必须加)
setInterval(() => { }, 60000 * 60);

//Math.ceil(Math.random()*5+10) 10到15随机整数
SilverFox0-6 · 11 小时前

回答

Hamibot AI
1
· 11 小时前

这个问题出现的原因是线程阻塞

原因分析

  1. UI 线程阻塞floaty.window 的按钮点击回调(如 w.start.click)运行在 UI 线程 上。UI 线程必须保持流畅以响应用户操作和绘制界面。
  2. 阻塞操作gesture() 是一个阻塞式的自动化操作,执行时需要等待手势完成。
  3. 冲突:当你在 UI 线程(或通过 UI 线程触发的回调)中直接调用 gesture(),UI 线程会被阻塞等待手势完成,导致界面无法刷新,出现“卡死”或“无响应”(ANR)。虽然你使用了 setTimeout,但在某些环境下,回调仍然可能关联到 UI 线程上下文,或者 gesture 执行时锁住了辅助服务通道,导致 UI 线程无法继续调度。

解决方案

  1. 使用子线程:将自动化任务(gesture、循环逻辑)放入 threads.start() 创建的子线程中运行,避免占用 UI 线程。
  2. 安全更新 UI:在子线程中修改悬浮窗内容(如 setText)时,必须使用 ui.run() 包裹,因为 UI 控件只能在 UI 线程修改。
  3. 语法兼容:Hamibot 使用的 JavaScript 引擎(Rhino 1.7.15)不完全支持 ES6 箭头函数 ()=>,建议全部改为 function() 以确保稳定性。

修正后的代码

以下是修复了线程问题并兼容引擎语法的代码:

// 文档地址 https://docs.hamibot.com/
auto.waitFor();

var running = false;
var taskThread = null;

// 循环执行的任务逻辑
function taskLoop() {
    while (running) {
        // 执行手势
        gesture(500, [576, 1360], [555, 865]);
        
        // 更新 UI 必须在 ui.run 中
        ui.run(function () {
            toastLog("执行一次任务");
        });

        // 随机延迟 1-5 秒
        var delay = Math.ceil(Math.random() * 5) * 1000;
        ui.run(function () {
            toastLog("下一次间隔为:" + delay);
        });
        
        // 子线程中使用 sleep 而不是 setTimeout 进行等待
        sleep(delay);
    }
}

// 悬浮窗界面
var w = floaty.window(
    <vertical padding="8" bg="#bababab4">
        <text id="running">停止中</text>
        <button id="start" text='开始' />
        <button id="btn1" text='停止' />
        <button id="btn3" text='退出' />
    </vertical>
);

// 开始按钮
w.start.click(function () {
    if (running) {
        toastLog("任务已经在执行!");
        return;
    }

    running = true;
    // 更新 UI
    ui.run(function () {
        w.running.setText("运行中");
    });
    
    toastLog("任务启动成功");
    
    // 启动子线程执行任务
    taskThread = threads.start(function () {
        // 延迟 2 秒后开始
        sleep(2000);
        taskLoop();
    });
});

// 停止按钮
w.btn1.click(function () {
    if (!running) {
        toastLog("没有任务在运行!");
        return;
    }

    running = false;
    w.running.setText("停止中");
    toastLog("任务已停止");
});

// 退出按钮
w.btn3.click(function () {
    running = false;
    w.close();
    engines.myEngine().forceStop();
});

// 悬浮窗基础设置
w.setAdjustEnabled(true);
w.setPosition(100, 100);
w.setSize(-2, -2);

// 脚本保活
setInterval(function () {}, 60000 * 60);

主要改动说明

  1. 线程分离:使用 threads.start() 包裹 taskLoop 函数。这样 gesture()sleep() 在子线程运行,不会卡住悬浮窗界面。
  2. UI 安全:所有涉及 w.running.setText()toastLog 的操作都放入了 ui.run() 中,防止子线程操作 UI 导致崩溃。
  3. 循环优化:将递归的 setTimeout 改为 while(running) 循环配合 sleep()。这是自动化脚本的标准写法,更稳定且易于控制停止状态。
  4. 语法修正:将所有箭头函数 ()=> 替换为 function(),兼容 Rhino 引擎,避免潜在的语法错误。

按照上述修改后,点击开始按钮不会再导致软件卡死。

ℹ️ 本回答由 AI 生成,内容仅供参考