Запуск позже будет более или менее влиять на поток пользовательского интерфейса. Выполнение будет поставлено в очередь в потоке пользовательского интерфейса. Когда у вас будет спящий поток или долгий расчет, пользовательский интерфейс будет зависать. Вы можете использовать его для простых задач. Но в вашем случае я рекомендую это, потому что это всего лишь «операция набора».
Обычно вы должны использовать Наблюдаемые свойства и связывать их с пользовательским интерфейсом, когда это возможно. Тогда вам не нужно ничего делать с операциями «Thread», «Queuing» и «RunLater».
Когда для выполнения операции ВСЕ нужно «запустить позже», а расчет будет дорогостоящим, вам следует поискать, что можно запустить позже, или использовать другой механизм.
Но когда вы просто добавляете что-то без выполнения позже, чтобы выполнить сложный или более длинный расчет из другого потока, появится исключение.
Итак, вы хотите обновить элементы пользовательского интерфейса, когда это необходимо, и новыйзначения вычисляются или предоставляются любым механизмом.
Здесь можно перейти к свойствам:
StringProperty text = new SimpleStringProperty("Hello");
Label label = new Label();
label.getTextProperty().bind(text);
Теперь, когда вы обновляете текст из другого потока, обновление будетавтоматически появляются в пользовательском интерфейсе без запуска позже.
Если иного пути нет:
Что вы могли бы (в случае более дорогостоящих операций) сделать, это иметь очередь, которая содержит все выполнения обновлений для представления, которые поступают асинхронно. И выполнять изменения только в потоке JavaFX.
Вы должны использовать Animationtimer для этого.
//thread safe queue
Queue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
Теперь подход с runLater
:
Platform.runLater(()->{
while(!queue.isEmpty()) {
queue.remove().run();
}
});
И подход с AnimationTimer.
AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
//Whatever condition or how many changes you would make at one tick
//In this case we just run all "updates" in that tick.
// I would recommend it, you could update 100 things each tick
while(!queue.isEmpty()) {
queue.remove().run();
}
}
};
timer.start();
Затем создайте поток, который будет что-то вычислять, и когда это будет сделано, он добавляет новое изменение в очередь. Или спит или ждет ответа и т. Д.
private void startTask() {
Runnable task = this::runTask;
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
private void runTask() {
for (int i = 1; i <= 10; i++) {
try {
String status = "Processing " + i + " of " + 10;
//Add the new change as a Runnable here
//It will be run in the in the next update of the UI
tasks.add(()->{
statusLabel.setText(status);
textArea.appendText(status + "\n");
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ПРИМЕЧАНИЕ
Все еще зависит, для каких целей вы собираетесь его использовать. Это всего лишь два подхода. Когда вы обновляете только объявленные значения, вы также можете использовать привязки свойств. (StringProperty в вашем случае)
И я бы лично всегда пошел с AnimationTimer. RunLater решает, когда он «в порядке» с очередью Runnable. С Animationtimer вы можете быть уверены, что он выполняется каждый тик, когда требуется обновление.
Вы также можете сохранять все обновления, а затем проводить опрос новых значений каждый тик.
ЗАДАЧА
Когда вы точно знаетегде вы хотите указать, какое значение вы должны использовать Task и связать его с элементом UI, это также будет работать.
Label statusLabel = new Label("Not Started...");
Task<String> t = new Task<String>() {
@Override
protected String call() throws Exception {
int i = 0;
while(//any condition ... ) {
i++;
Thread.sleep(1000);
updateValue("i is " + i);
}
return "last value!";
}
// now Bind the TextProperty of the Label to the ValueProperty of the
// Task
statusLabel.getTextproperty().bind(task.getValueProperty());
Thread backgroundThread = new Thread(task);
backgroundThread.start();