JavaFX定期后台任务
我尝试定期在JavaFX应用程序后台线程中运行,这会修改一些GUI属性 我想我知道如何使用JavaFX定期后台任务,javafx,Javafx,我尝试定期在JavaFX应用程序后台线程中运行,这会修改一些GUI属性 我想我知道如何使用javafx.concurrent中的Task和Service类,如果不使用Thread\sleep()方法,我就无法理解如何运行这样的周期性任务。如果我可以使用Executors中的Executor制作方法(Executors.newSingleThreadScheduledExecutor()),那就太好了 我试图每5秒运行一次Runnable,这会重新启动javafx.concurrent.Servi
javafx.concurrent
中的Task
和Service
类,如果不使用Thread\sleep()
方法,我就无法理解如何运行这样的周期性任务。如果我可以使用Executors
中的Executor
制作方法(Executors.newSingleThreadScheduledExecutor()
),那就太好了
我试图每5秒运行一次Runnable
,这会重新启动javafx.concurrent.Service
,但它会立即挂起,因为Service.restart
甚至调用了Service.getState()
因此,最后我使用了Executors.newSingleThreadScheduledExecutor()
,它每5秒启动一次Runnable
,并且Runnable
运行另一个Runnable
,使用:
Platform.runLater(new Runnable() {
//here i can modify GUI properties
}
这看起来很糟糕:(有没有更好的方法使用
任务
或服务
类来完成此任务?您可以使用时间线来完成此任务:
Timeline fiveSecondsWonder = new Timeline(
new KeyFrame(Duration.seconds(5),
new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("this is called every 5 seconds on UI thread");
}
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();
您可以使用该任务的时间线:
Timeline fiveSecondsWonder = new Timeline(
new KeyFrame(Duration.seconds(5),
new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("this is called every 5 seconds on UI thread");
}
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();
下面是一个使用Java 8和的解决方案。假设您希望定期重新计算
Label.textProperty()
的值
Label=。。。;
EventStreams.ticks(Duration.ofSeconds(5))//发出周期性的ticks
.supplyCompletionStage(()->getStatusAsync())//在每个刻度上启动后台任务
.await()//准备就绪时发出任务结果
.subscribe(label::setText);//为每个结果执行label.setText()
CompletionStage getStatusAsync(){
返回CompletableFuture.SupplySync(()->getStatusFromNetwork());
}
字符串getStatusFromNetwork(){
// ...
}
与Sergey的解决方案相比,您没有将整个线程用于从网络获取状态,而是使用共享线程池来获取状态。这里有一个使用Java 8和的解决方案。假设您希望定期重新计算
Label.textProperty()的值
Label=。。。;
EventStreams.ticks(Duration.ofSeconds(5))//发出周期性的ticks
.supplyCompletionStage(()->getStatusAsync())//在每个刻度上启动后台任务
.await()//准备就绪时发出任务结果
.subscribe(label::setText);//为每个结果执行label.setText()
CompletionStage getStatusAsync(){
返回CompletableFuture.SupplySync(()->getStatusFromNetwork());
}
字符串getStatusFromNetwork(){
// ...
}
与Sergey的解决方案相比,您没有将整个线程用于从网络获取状态,而是使用共享线程池来获取状态。我更喜欢PauseTransition:
PauseTransition wait=新的PauseTransition(持续时间.秒(5));
等等,setOnFinished((e)->{
/*你的方法*/
等等,播放fromstart();
});
等等,play();
我更喜欢暂停转换:
PauseTransition wait=新的PauseTransition(持续时间.秒(5));
等等,setOnFinished((e)->{
/*你的方法*/
等等,播放fromstart();
});
等等,play();
您也可以使用。我在使用此选项时注意到,在使用时间线和暂停转换期间,我的应用程序中出现了一些UI冻结,特别是当用户与菜单栏的元素交互时(在JavaFX12上)。使用ScheduledService
这些问题不再发生
class UpdateLabel extends ScheduledService<Void> {
private Label label;
public UpdateLabel(Label label){
this.label = label;
}
@Override
protected Task<Void> createTask(){
return new Task<Void>(){
@Override
protected Void call(){
Platform.runLater(() -> {
/* Modify you GUI properties... */
label.setText(new Random().toString());
});
return null;
}
}
}
}
您也可以使用。我在使用此选项时注意到,在使用时间线
和暂停转换
期间,我的应用程序中出现了一些UI冻结,特别是当用户与菜单栏
的元素交互时(在JavaFX12上)。使用ScheduledService
这些问题不再发生
class UpdateLabel extends ScheduledService<Void> {
private Label label;
public UpdateLabel(Label label){
this.label = label;
}
@Override
protected Task<Void> createTask(){
return new Task<Void>(){
@Override
protected Void call(){
Platform.runLater(() -> {
/* Modify you GUI properties... */
label.setText(new Random().toString());
});
return null;
}
}
}
}
前言:对于询问如何在JavaFX中执行定期操作、是否应该在后台执行操作的问题,这个问题通常是重复的目标。虽然这个问题已经有了很好的答案,但这个答案试图整合所有给定的信息(以及更多)归纳成单一答案,并解释/显示每种方法之间的差异
这个答案关注JavaSE和JavaFX中可用的API,而不是第三方库,如ReactFX(如中所示)
背景信息:JavaFX和Threads
与大多数主流GUI框架一样,JavaFX是单线程的。这意味着有一个线程专门用于读写UI状态和处理用户生成的事件(例如鼠标事件、按键事件等)。在JavaFX中,该线程称为“JavaFX应用程序线程”,有时简称为“FX线程”,但其他框架可能会称之为其他名称。其他一些名称包括“UI线程”、“事件调度线程”和“主线程”
绝对重要的是,任何连接到屏幕上显示的GUI的内容都只能在JavaFX应用程序线程上访问或操作。JavaFX框架不是线程安全的,使用不同的线程不正确地读取或写入UI状态可能会导致未定义的行为。即使您没有看到任何外部可见的问题是,在没有中断代码的情况下访问线程之间共享的状态
但是,许多GUI对象可以在任何线程上进行操作,只要它们不是“活动的”
节点对象可以在任何线程上构造和修改,只要它们尚未附加到窗口中的场景
(即
[emphasis added])。应用程序必须将节点附加到这样的场景或在JavaFX应用程序线程上修改它们
但是其他GUI对象,例如窗口
,甚至节点
的一些子类(例如WebView
),都是
class WindowController implements Initializable {
private @FXML Label randomNumber;
@Override
public void initialize(URL u, ResourceBundle res){
var service = new UpdateLabel(randomNumber);
service.setPeriod(Duration.seconds(2)); // The interval between executions.
service.play()
}
}