JavaFX.Scene.Robot 崩溃/UI 冻结

问题描述 投票:0回答:2

我正在尝试编写一个简单的 JavaFX 应用程序,它充当我玩的游戏的自动点击器。我选择鼠标交替单击的两个点。一切工作正常,直到机器人需要完成他的工作。 当我这样说时:

robot.mouseMove(join);
Thread.sleep(2000);
robot.mouseClick(MouseButton.PRIMARY);
Thread.sleep(2000);
robot.mouseMove(accept);
Thread.sleep(2000);
robot.mouseClick(MouseButton.PRIMARY);
Thread.sleep(2000);

我的应用程序崩溃了。我在网上阅读了一些内容,看来您不应该在 JavaFX 应用程序线程中睡觉。我的新方法是创建一个新线程来处理应用程序线程中的单击,如下所示:

clicker = new Clicker(join, accept);
Thread clickerThread = new Thread(clicker);
clickerThread.start();

这是它在 Clicker 中的外观:

 public void run() {
        while (running){
            try {
                robot.mouseMove(join);
                Thread.sleep(2000);
                robot.mouseClick(MouseButton.PRIMARY);
                Thread.sleep(2000);
                robot.mouseMove(accept);
                Thread.sleep(2000);
                robot.mouseClick(MouseButton.PRIMARY);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Clicker sleep interrupted!");
            }
        }
    }

但是,使用新方法时,我突然收到此错误:

线程“Thread-3”中的异常 java.lang.IllegalStateException:仅在事件线程上允许此操作;当前线程 = 线程 3

有谁知道我该如何解决这个问题?

multithreading user-interface javafx sleep awtrobot
2个回答
1
投票

当您想在 JavaFX 应用程序线程 上执行定期前台任务时,您应该首先考虑使用动画。这是使用

Timeline
的示例:

Point2D join = ...;
Point2D accept = ...;
Robot robot = ...;

Timeline timeline = new Timeline(
    new KeyFrame(Duration.ZERO, e -> robot.mouseMove(join)),
    new KeyFrame(Duration.seconds(2), e -> robot.mouseClick(MouseButton.PRIMARY)),
    new KeyFrame(Duration.seconds(4), e -> robot.mouseMove(accept)),
    new KeyFrame(Duration.seconds(6), e -> robot.mouseClick(MouseButton.PRIMARY)),
    new KeyFrame(Duration.seconds(8))
);
timeline.play();

上面的代码将在前一个处理程序之后两秒执行每个

KeyFrame
的已完成处理程序(动画开始后立即执行第一个处理程序)。最后一个
KeyFrame
与您对
sleep
的最后一次调用相匹配,尽管这可能不是必需的。所有这一切都将发生在 JavaFX 应用程序线程

您可以通过设置动画的

cycleCount
属性将动画配置为重播一定次数,甚至永远重播。例如:

timeline.setCycleCount(5); // play 5 times then stop
// or
timeline.setCycleCount(Animation.INDEFINITE); // play forever

0
投票

您需要在 UI 线程上执行 mouseMove 尝试用

Platform.runlater(() -> robot.mouseMove(MouseButton.PRIMARY));

包装调用
public void run() {
        while (running){
            try {
                Platform.runlater(() -> robot.mouseMove(join));
                Thread.sleep(2000);
                Platform.runlater(() -> robot. mouseClick(MouseButton.PRIMARY));
                Thread.sleep(2000);
                Platform.runlater(() -> robot.mouseMove(accept));
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Clicker sleep interrupted!");
            }
        }
    }

Platform.runLater()
将在主线程上安排一个操作并立即返回。实际执行操作可能需要一些时间,在小型应用程序中,这种延迟通常是不可察觉的。如果延迟很长,您可能会在执行第一个操作之前安排您的操作、睡眠并再次安排。如果您希望 ClickerThread 在继续之前等待操作执行,那么您将需要某种形式的同步,请参阅下面的使用锁包装
Platform.runlater()
的方法示例。

public void waitUntilExecutedOnMainThread(Runnable runnable){
    Semaphore semaphore = new Semaphore(0);
    Platform.runLater(() -> {
        try {
            runnable.run();
        } finally {
            semaphore.release();
        }
    });
    try {
        semaphore.acquire();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

waitUntilExecutedOnMainThread(() -> robot. mouseClick(MouseButton.PRIMARY))
© www.soinside.com 2019 - 2024. All rights reserved.