问题
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
回答1:
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.
private Logger logger = Logger.getLogger("LoggerUI");
and use as follows:
logger.addHandler(new Handler() { ... });
logger.log(Level.SEVERE, "whatever" +j);
Also, I would advise to move Platform.runLater()
call to publish()
methods, so your logger will work from any thread.
来源:https://stackoverflow.com/questions/12339853/logger-entries-translated-to-the-ui-stops-being-updated-with-time