简体   繁体   English

JavaFX任务结束和JavaFX线程化

[英]JavaFX Task ending and JavaFX threading

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. 我今天才刚刚开始学习JavaFX,我试图通过制作Snake克隆来了解更多有关JavaFX的信息,但是我在线程方面遇到了麻烦。 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. 我想创建一个线程来更新蛇在屏幕上的位置,但是无法以正常的Runnable线程方式使用它,因为我正在该线程内使用JavaFX来更新绘制到屏幕上的矩形的位置(这是我了解到的您不能做,而必须使用Tasks,Services,Platform.runLater等?)我从中创建线程的类扩展了JavaFX.scene.layout.Pane,而我正在尝试使用任务来更新蛇的位置。 My problem is: The task only seems to run once or twice and quits, without me giving any interrupts. 我的问题是:该任务似乎只运行了一次或两次并退出,而我没有给出任何中断。

Constructor for the class that extends Pane (The Snake class extends Group): 扩展Pane的类的构造函数(Snake类扩展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();
}

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? 关于我应该做什么或如何解决这个问题,有什么建议吗?

The basic rules for threading in JavaFX (and forgive me if you already understand some of this, I just want to be complete) are: JavaFX中线程化的基本规则(如果您已经了解其中的一些内容,请问完整,请原谅我):

  1. Anything that blocks execution (or takes a long time to execute) should be run on a background thread - not on the FX Application Thread 任何阻止执行(或执行时间长)的操作都应在后台线程上运行-而不是在FX Application线程上运行
  2. Anything that changes the state of a Node that is part of the scene graph should be executed on the FX Application Thread 任何更改作为场景图一部分的Node的状态的操作都应在FX Application线程上执行

In order to help achieve these, the JavaFX API provides a Task class. 为了帮助实现这些目的,JavaFX API提供了一个Task类。 This has a call() method that returns a value; 它具有一个返回值的call()方法; it is a Runnable so can be provided as the argument to a Thread constructor, or passed to an Executor . 它是一个Runnable因此可以作为Thread构造函数的参数提供,也可以传递给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. 它还提供了保证在FX Application Thread上执行的有用回调,例如setOnSucceededsetOnFailed和各种update...()方法,这些属性用于更新FX Application Thread上的progressmessage等属性。

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. 但是, Task类实际上是为一次性任务而设计的:例如,考虑需要从数据库检索数据的应用程序,这可能会花费一些时间。 These execute a specific action and return a result. 它们执行特定的操作并返回结果。 Your case is somewhat different, in that your thread is executing continuously. 您的情况有所不同,因为您的线程正在连续执行。

In this case, it's probably better to use a simple Thread and use Platform.runLater(...) to update the UI. 在这种情况下,最好使用简单的Thread并使用Platform.runLater(...)更新UI。 Platform.runLater(...) takes a Runnable and executes its run() method on the FX Application Thread. Platform.runLater(...)接受一个Runnable并在FX Application Thread上执行其run()方法。

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. 我不清楚您的代码为什么会如此描述,但是假设方法调用snake.updatePosition()导致UI发生更改,应在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;
                }
            }
    }
});

If you are using Java 8, this all looks a lot nicer with lambdas replacing the anonymous inner classes: 如果您使用的是Java 8,则用lambda替换匿名内部类会更好一些:

taskThread = new Thread( () -> {
    while (! Thread.currentThread().isInterrupted()) {
        Platform.runLater( snake::updatePosition );
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exc) {
            break ;
        }
    }
});

One other technique in JavaFX for executing something periodically is to (ab?)use an Animation: JavaFX中用于定期执行某些操作的另一种技术是(ab?)使用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();

or, in Java 8, the somewhat slick 或者,在Java 8中,略显光滑

    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), 
        event -> snake.updatePosition()));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM