问题描述
我今天才开始学习JavaFX,我试图通过制作Snake克隆来了解更多信息,但我遇到了线程问题。我想创建一个更新蛇在屏幕上的位置的线程,但不能以正常的Runnable线程方式使用它,因为我在该线程中使用JavaFX更新绘制到屏幕的矩形位置(我学到了你不能这样做,而且必须改为使用Tasks,Services,Platform.runLater等?)我创建线程的类扩展了JavaFX.scene.layout.Pane,我试图用一个任务来更新蛇的位置。我的问题是:任务似乎只运行一次或两次并退出,没有我给出任何中断。
I only just started learning about JavaFX today and I was trying to learn more about it by making a Snake clone, but I am having trouble with threading. I wanted to create a thread that updated the snake's position on the screen, but couldn't use one in a normal Runnable thread way, because I was using JavaFX from within that thread to update positions of rectangles drawn to the screen (Which I learned you cannot do, and must instead use Tasks, Services, Platform.runLater, etc?) My class that I was creating the thread from extends JavaFX.scene.layout.Pane, and I am trying to use a task to update the snake position. My problem is: The task only seems to run once or twice and quits, without me giving any interrupts.
扩展Pane的类的构造函数(The Snake类扩展Group ):
Constructor for the class that extends Pane (The Snake class extends Group):
public GameFrame(){
this.setPrefSize(800, 600);
Snake snake = new Snake();
this.getChildren().add(snake);
taskThread = new Thread(new Task<Void>() {
protected Void call() throws Exception {
while(!Thread.currentThread().isInterrupted()){
snake.updatePosition();
try{
Thread.sleep(1000);
} catch(InterruptedException e){
break;
}
}
return null;
}
});
taskThread.start();
}
我觉得我实际上并没有抓住这里最好的事情是的,我想做的可能是hackish。有什么建议我应该做什么,或者我如何解决这个问题?
I feel like I don't actually grasp what the best thing to do here is, and what I am trying to do may be hackish. Are there any suggestions on what I should do, or how I could fix this?
推荐答案
JavaFX中线程的基本规则(原谅我,如果你已经理解其中一些,我只想完成)是:
The basic rules for threading in JavaFX (and forgive me if you already understand some of this, I just want to be complete) are:
- 阻止执行的任何事情(或者执行的时间很长)应该在后台线程上运行 - 而不是在FX应用程序线程上运行
- 任何改变
节点那是场景图的一部分应该在FX应用程序线程上执行
Anything that blocks execution (or takes a long time to execute) should be run on a background thread - not on the FX Application Thread
Anything that changes the state of a
Node
that is part of the scene graph should be executed on the FX Application Thread
为了帮助实现这些,JavaFX API提供了一个
任务
类。这有一个返回值的 call()
方法;它是 Runnable
所以可以作为 Thread
构造函数的参数提供,或者传递给执行人
。它还提供有用的回调,保证在FX应用程序线程上执行,例如 setOnSucceeded
, setOnFailed
,以及各种更新...()
更新属性的方法,例如进度
和消息。
In order to help achieve these, the JavaFX API provides a
Task
class. This has a call()
method that returns a value; it is a Runnable
so can be provided as the argument to a Thread
constructor, or passed to an Executor
. It also provides useful callbacks that are guaranteed to be executed on the FX Application Thread, such as setOnSucceeded
, setOnFailed
, and various update...()
methods that update properties such as progress
and message
on the FX Application Thread.
然而,
任务
类实际上是为一个人设计的 - 关闭任务:考虑需要从数据库检索数据的应用程序,例如,这可能需要一些时间。这些执行特定操作并返回结果。你的情况有所不同,因为你的线程正在连续执行。
However, the
Task
class is really designed for one-off tasks: think about applications that need to retrieve data from a database, for example, which may take time. These execute a specific action and return a result. Your case is somewhat different, in that your thread is executing continuously.
在这种情况下,最好使用一个简单的
Thread
并使用 Platform.runLater(...)
更新UI。 Platform.runLater(...)
获取 Runnable
并执行其 run()
FX应用程序线程上的方法。
In this case, it's probably better to use a simple
Thread
and use Platform.runLater(...)
to update the UI. Platform.runLater(...)
takes a Runnable
and executes its run()
method on the FX Application Thread.
我不清楚为什么你的代码表现得像你描述的那样,但假设方法调用
snake.updatePosition()
导致UI发生变化,应该在FX Application Thread上执行。在任何情况下我都会尝试
It's not clear to me why your code is behaving the way you describe, but assuming the method call
snake.updatePosition()
causes a change in the UI, that should be executed on the FX Application Thread. In any case I would try
taskThread = new Thread(new Runnable() {
public void run() {
while(!Thread.currentThread().isInterrupted()){
Platform.runLater(new Runnable() {
@Override
public void run() {
snake.updatePosition();
}
});
try{
Thread.sleep(1000);
} catch(InterruptedException e){
break;
}
}
}
});
如果您使用的是Java 8,那么使用lambdas替换匿名内部类会更好看:
If you are using Java 8, this all looks a lot nicer with lambdas replacing the anonymous inner classes:
taskThread = new Thread( () -> {
while (! Thread.currentThread().isInterrupted()) {
Platform.runLater( snake::updatePosition );
try {
Thread.sleep(1000);
} catch (InterruptedException exc) {
break ;
}
}
});
JavaFX中用于定期执行某事的另一种技巧是(ab?)使用动画:
One other technique in JavaFX for executing something periodically is to (ab?)use an Animation:
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1),
new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
snake.updatePosition();
}
}
));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
或者,在Java 8中,有点光滑
or, in Java 8, the somewhat slick
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1),
event -> snake.updatePosition()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
这篇关于JavaFX任务结束和JavaFX线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!