Reputation: 296
I've got a main window and when I press a button on it, another window will open.
After a certain amount of time, this window should close. Because it extends stage
I use super.close
, but it doesn't work. What can I do? Can it be caused because I close it inside a service task?
public Game(int width, int height, int time) {
this.width = width - 15;
this.height = height - 15;
this.time = time;
this.layout = generateLayout();
this.scene = new Scene(this.layout, this.width, this.height);
super.initModality(Modality.APPLICATION_MODAL);
super.setScene(scene);
super.setResizable(false);
super.setTitle(TITLE);
this.cicle.reset();
this.cicle.start();
super.showAndWait();
}
and inside the cicle
I call close()
It's a bit confused, but this is the cicle
service task:
Service<Void> cicle = new Service<Void>() {
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
volatile boolean waiting = false;
@Override
protected Void call() throws Exception {
Timer.start();
while (Timer.update() / 1000 < time) {
System.out.println(Timer.update())
}
close();
return null;
}
};
}
};
Upvotes: 0
Views: 1460
Reputation: 46170
The issue is you are attempting to close the Stage
from a thread other than the JavaFX Application Thread. Looking at the documentation of Stage
you'll see:
Stage objects must be constructed and modified on the JavaFX Application Thread.
The JavaFX Application Thread is created as part of the startup process for the JavaFX runtime. See the Application class and the Platform.startup(Runnable) method for more information.
Yet a Service
, when started, will execute its Task
on a background thread. Specifically, the call()
method of the Task
is executed on said background thread. I just tested trying to close a Stage
on a background thread to see what happens (whether or not an exception would be thrown). The result was an java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
.
The reason you aren't seeing this is you aren't observing the Service
for failure. You can look at the answer to this question to see ways to watch for failure. The answer specifically talks about Task
but the same behavior is present in Service
.
What you need to do to make your code work is to make sure that close()
is called on the JavaFX Application Thread. The easiest way to do this would be to wrap close()
in a Platform.runLater(Runnable)
call.
Service<Void> cicle = new Service<Void>() {
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
volatile boolean waiting = false;
@Override
protected Void call() throws Exception {
Timer.start();
while (Timer.update() / 1000 < time) {
System.out.println(Timer.update())
}
Platform.runLater(() -> close()); // wrapped in Platform#runLater
return null;
}
};
}
};
You could also override Service.succeeded()
which will always be called on the JavaFX Application Thread (but only if the Service
succeeds). In this case, you'd remove the call to close()
from the call()
method.
Service<Void> cicle = new Service<Void>() {
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
volatile boolean waiting = false;
@Override
protected Void call() throws Exception {
Timer.start();
while (Timer.update() / 1000 < time) {
System.out.println(Timer.update())
}
return null;
}
};
}
@Override
protected void succeeded() {
close();
}
};
There are other ways as well: listening to the state
property, using setOnSucceeded(EventHandler)
, etc...
Upvotes: 1