I have a javafx.concurrent.Task that run some code in background and use the java.util.logging.Logger to give information about its status. I need to show this log entry in the UI in the main thread. how can I do this?
this is a simple toy project I've made to solve the problem out of the context
@Override
public void start(Stage primaryStage) {
ListView<String> list = new ListView<>();
final ObservableList<String> items =FXCollections.observableArrayList ();
list.setItems(items);
Task<String> task = new Task<String>() {
@Override
protected String call() throws Exception {
doStuff();
return "yey";
}
};
new Thread(task).start();
Scene scene = new Scene(list, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void doStuff() {
for (int i=0;i<10;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger("LoggerUI").log(Level.INFO, "error");
}
Logger.getLogger("LoggerUI").log(Level.SEVERE, "whatever");
}
[...]
}
I've tried this but it doesn't work
public class LoggerUI extends Application {
@Override
public void start(Stage primaryStage) {
System.out.println("UI"+ Thread.currentThread().getId());
ListView<String> list = new ListView<>();
final ObservableList<String> items =FXCollections.observableArrayList ();
list.setItems(items);
Logger.getLogger("LoggerUI").addHandler(new Handler() {
@Override
public void publish(LogRecord lr) {
System.out.println("publish()"+ Thread.currentThread().getId());
items.add(lr.getMessage());
}
@Override
public void flush() {
System.out.println("flush");
}
@Override
public void close() throws SecurityException {
System.out.println("close");
}
});
Task<String> task = new Task<String>() {
@Override
protected String call() throws Exception {
doStuff();
return "yey";
}
};
new Thread(task).start();
Scene scene = new Scene(list, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void doStuff() {
System.out.println("doStuff()"+ Thread.currentThread().getId());
for (int i=0;i<300;i++) {
final int j =i;
try {
Thread.sleep(30);
} catch (InterruptedException ex) {
Logger.getLogger("LoggerUI").log(Level.INFO, "error");
}
Platform.runLater(new Runnable() {
@Override
public void run() {
System.out.println("runLater()"+ Thread.currentThread().getId());
Logger.getLogger("LoggerUI").log(Level.SEVERE, "whatever" +j);
}
});
}
[..]
}
}
it shows the first 20-30th entries and then stop to update the UI
Update: It's not actually JavaFX issue, but java logging's one.
Store Logger into variable, because Loggers returned by
Logger.getLogger()
are not the same each time, they are cached, garbage collected and stuff.and use as follows:
Also, I would advise to move
Platform.runLater()
call topublish()
methods, so your logger will work from any thread.