所以我有一个JavaFX8应用程序,我正在尝试创建在每次迭代时更改GUI元素的计划线程。我正在使用ScheduledExecutorService,但是如果run()方法中的任何代码引用了FXML对象,它就会死掉而没有任何错误。
此代码位于我的主FXML窗口的Controller中。 fxmlLabel声明为@FXML private Label fxmlLabel;
此代码有效,并正确打印" Hello World!"每一秒。
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Hello world!");
}
};
executor.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
此代码打印" Hello World!"曾经,没有任何反应。
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Hello world!");
fxmlLabel.setText("Bye world!");
System.out.println("Hello again!");
}
};
executor.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
答案 0 :(得分:4)
您无法从后台线程更改作为显示的场景图形一部分的UI元素的状态。在Java 8中,这会引发异常。在早期版本的JavaFX中,它可能会抛出异常或者可能会无声地失败。
要解决此问题,请在Platform.runLater(...)
中包装更新场景图的调用:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class UpdateLabelRegularly extends Application {
@Override
public void start(Stage primaryStage) {
Label label = new Label();
VBox root = new VBox();
root.getChildren().add(label);
ScheduledExecutorService exec = Executors.newScheduledThreadPool(5, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
AtomicInteger count = new AtomicInteger();
Runnable task = () -> {
System.out.println("Hello world!");
Platform.runLater(() -> label.setText("Count: "+count.incrementAndGet()));
System.out.println("Hello again");
};
exec.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
Scene scene = new Scene(root, 250, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
或使用javafx.concurrent.ScheduledService
类:
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class UpdateLabelRegularly extends Application {
@Override
public void start(Stage primaryStage) {
Label label = new Label();
VBox root = new VBox();
root.getChildren().add(label);
AtomicInteger count = new AtomicInteger();
ScheduledService<String> service = new ScheduledService<String>() {
@Override
protected Task<String> createTask() {
Task<String> task = new Task<String>() {
@Override
public String call() {
return "Count: "+count.incrementAndGet();
}
};
return task ;
}
};
service.setOnSucceeded(event -> label.setText(service.getValue()));
service.setDelay(Duration.seconds(1));
service.setPeriod(Duration.seconds(1));
service.start();
Scene scene = new Scene(root, 250, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
一般来说,第二种方法更好(imho),因为它在任务逻辑和完成时UI的更新之间提供了很好的分离。有关更多示例,请参阅Javadocs for Task
和ScheduledService
。
还有一次更新:
为了完整起见,这里有一个使用ScheduledExecutorService
和javafx.concurrent
API的版本,以防万一您有被迫这样做的原因:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class UpdateLabelRegularly extends Application {
@Override
public void start(Stage primaryStage) {
Label label = new Label();
VBox root = new VBox();
root.getChildren().add(label);
AtomicInteger count = new AtomicInteger();
ScheduledExecutorService exec = Executors.newScheduledThreadPool(5, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
exec.scheduleAtFixedRate(() -> {
Task<String> task = new Task<String>() {
@Override
public String call() {
return "Count: "+count.incrementAndGet();
}
};
task.setOnSucceeded(event -> label.setText(task.getValue()));
task.run();
}, 1, 1, TimeUnit.SECONDS);
Scene scene = new Scene(root, 250, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}