我正在尝试编写一个简单的 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
有谁知道我该如何解决这个问题?
当您想在 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
您需要在 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))