我是一名初学Java程序员,试图解决这个问题。我有一段代码可以进行一些计算并在我的JavaFX GUI中更新标签。它使用100ms
和ScheduledExecutorService
每隔Runnable
运行一次。问题是它无法更新GUI的Label
。我昨天一直在寻找一种方法来实现它,大多数主题似乎都是通过使用Platform.runLater
来解决的,但即使将我的代码放入runLater
runnable似乎仍然无效。我发现的另一件事是使用Java并发框架,但我不知道如何将它用于像这样的重复调度服务。这是我编写代码的方式:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable loop = new Runnable() {
public void run() {
Platform.runLater(new Runnable() {
@Override public void run() {
double result = calculation();
labelResult.setText("" + result);
}
});
}
};
executor.scheduleAtFixedRate(loop, 0, 100, TimeUnit.MILLISECONDS);
我怎么能这样做?
编辑: 我包括一个完整的例子。 主要课程:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
public class Main{
private static long value = 0;
private static Gui gui;
public static void main(String[] args){
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable loop = new Runnable() {
public void run() {
Platform.runLater(new Runnable() {
@Override public void run() {
calculate();
}
});
}
};
executor.scheduleAtFixedRate(loop, 0, 100, TimeUnit.MILLISECONDS);
Application.launch(Gui.class, args);
}
public static void calculate(){
double result = value++;
gui.setResult(result);
}
public static void setGui(Gui ref){
gui = ref;
}
}
桂班:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Gui extends Application{
private Stage window;
private Scene scene;
private HBox layout = new HBox();
private Label result = new Label("TEST");
@Override
public void start(Stage stage) throws Exception {
window = stage;
layout.getChildren().addAll(result);
Main.setGui(this);
scene = new Scene(layout, 1280, 720);
window.setTitle("Example");
window.setResizable(false);
window.setScene(scene);
window.show();
}
public void setResult(double res){
result.setText("" + res);
}
}
答案 0 :(得分:1)
您的应用程序的整体结构是错误的。您的计划执行程序服务失败的原因是您在启动JavaFX应用程序之前尝试启动它,因此您在FX工具包启动之前和FX应用程序线程运行之前首次调用Platform.runLater(...)
。
如果您将来自Platform.runLater()
块中的try
以及catch
的例外情况包裹起来,那么例外:
Runnable loop = new Runnable() {
public void run() {
try {
Platform.runLater(new Runnable() {
@Override
public void run() {
calculate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
您将看到异常:
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at javafx.application.Platform.runLater(Platform.java:83)
at Main$1.run(Main.java:17)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
(顺便说一下,处理异常也会让执行者继续,所以最终它会“恢复”,因为工具包会在某个时刻启动。你也可能会看到其他异常,因为,例如,有竞争条件gui
字段:在gui
初始化之前,可能会调用执行程序的某些迭代。)
您应该将Application.start()
方法视为应用程序的入口点。当您调用launch()
时(或在大多数最终部署方案中为您调用它时),FX Toolkit将启动,然后创建Application
子类的实例,并{{1在FX应用程序线程上的该实例上调用。
所以构建它的方法是从start()
方法驱动它。在那里创建GUI类的实例,创建运行预定执行程序的类的实例,将它们绑定在一起,然后在提供的阶段中显示UI。以下是此重构的一个可能示例:
Main.java:
start()
UpdateService.java:
import javafx.application.Application;
import javafx.stage.Stage;
public class Main extends Application{
private Stage window;
@Override
public void start(Stage stage) throws Exception {
window = stage;
Gui gui = new Gui();
UpdateService service = new UpdateService(gui);
service.startService();
window.setTitle("Example");
window.setResizable(false);
window.setScene(gui.getScene());
window.show();
}
public static void main(String[] args) {
launch(args);
}
}
Gui.java:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Platform;
public class UpdateService {
private long value = 0;
private final Gui gui;
public UpdateService(Gui gui) {
this.gui = gui;
}
public void startService() {
// create executor that uses daemon threads;
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
});
Runnable loop = new Runnable() {
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
calculate();
}
});
}
};
executor.scheduleAtFixedRate(loop, 0, 100, TimeUnit.MILLISECONDS);
}
public void calculate() {
double result = value++;
gui.setResult(result);
}
}
最后,请注意,在FX应用程序线程上运行定期重复功能的更简洁方法是使用动画API(如JavaFX periodic background task中所示):
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class Gui {
private Scene scene;
private HBox layout = new HBox();
private Label result = new Label("TEST");
public Gui() {
layout.getChildren().addAll(result);
scene = new Scene(layout, 1280, 720);
}
public Scene getScene() {
return scene ;
}
public void setResult(double res){
result.setText("" + res);
}
}