天天看點

Java線程更新ui_從JavaFX中的不同線程更新UI

不知道我是否完全了解,但我認為這可能有幫助。

使用Platform.runLater(…)是一個适當的方法。

避免泛濫FX應用程式線程的技巧是使用Atomicvariables來存儲您感興趣的值。在Platform.runLater(…)方法中,檢索它并将其設定為标記值。 在你的背景線程中,更新Atomicvariables,但是如果它已經被設定回它的sentinel值,隻釋出一個新的Platform.runLater(…)。

我通過檢視Task的源代碼來了解這一點。 看看updateMessage(..)方法(在編寫本文時為1131行)是如何實作的。

這是一個使用相同技術的例子。 這隻是一個(繁忙)背景線程計數盡可能快,更新一個IntegerProperty。 觀察者觀察該屬性并用新值更新AtomicInteger。 如果AtomicInteger的目前值是-1,則排程一個Platform.runLater()。

在Platform.runLater中,我檢索AtomicInteger的值并使用它來更新一個Label,并在該過程中将該值設定回-1。 這表示我已準備好進行其他UI更新。

import java.text.NumberFormat; import java.util.concurrent.atomic.AtomicInteger; import javafx.application.Application; import javafx.application.Platform; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; public class ConcurrentModel extends Application { @Override public void start(Stage primaryStage) { final AtomicInteger count = new AtomicInteger(-1); final AnchorPane root = new AnchorPane(); final Label label = new Label(); final Model model = new Model(); final NumberFormat formatter = NumberFormat.getIntegerInstance(); formatter.setGroupingUsed(true); model.intProperty().addListener(new ChangeListener() { @Override public void changed(final ObservableValue extends Number> observable, final Number oldValue, final Number newValue) { if (count.getAndSet(newValue.intValue()) == -1) { Platform.runLater(new Runnable() { @Override public void run() { long value = count.getAndSet(-1); label.setText(formatter.format(value)); } }); } } }); final Button startButton = new Button("Start"); startButton.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { model.start(); } }); AnchorPane.setTopAnchor(label, 10.0); AnchorPane.setLeftAnchor(label, 10.0); AnchorPane.setBottomAnchor(startButton, 10.0); AnchorPane.setLeftAnchor(startButton, 10.0); root.getChildren().addAll(label, startButton); Scene scene = new Scene(root, 100, 100); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } public class Model extends Thread { private IntegerProperty intProperty; public Model() { intProperty = new SimpleIntegerProperty(this, "int", 0); setDaemon(true); } public int getInt() { return intProperty.get(); } public IntegerProperty intProperty() { return intProperty; } @Override public void run() { while (true) { intProperty.set(intProperty.get() + 1); } } } }

如果你真的想從UI中“驅動”後端:那就是節制後端實作的速度,以便看到所有的更新,可以考慮使用一個AnimationTimer 。 一個AnimationTimer有一個handle(...) ,每幀渲染一次。 是以,您可以阻止後端實作(例如使用阻塞隊列),并在每次調用handle方法時釋放一次。 FX應用程式線程調用handle(...)方法。

handle(...)方法需要一個時間戳(納秒)的參數,是以你可以使用它來進一步減慢更新,如果每幀一次太快的話。

例如:

import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.beans.property.LongProperty; import javafx.beans.property.SimpleLongProperty; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; public class Main extends Application { @Override public void start(Stage primaryStage) { final BlockingQueue messageQueue = new ArrayBlockingQueue<>(1); TextArea console = new TextArea(); Button startButton = new Button("Start"); startButton.setOnAction(event -> { MessageProducer producer = new MessageProducer(messageQueue); Thread t = new Thread(producer); t.setDaemon(true); t.start(); }); final LongProperty lastUpdate = new SimpleLongProperty(); final long minUpdateInterval = 0 ; // nanoseconds. Set to higher number to slow output. AnimationTimer timer = new AnimationTimer() { @Override public void handle(long now) { if (now - lastUpdate.get() > minUpdateInterval) { final String message = messageQueue.poll(); if (message != null) { console.appendText("\n" + message); } lastUpdate.set(now); } } }; timer.start(); HBox controls = new HBox(5, startButton); controls.setPadding(new Insets(10)); controls.setAlignment(Pos.CENTER); BorderPane root = new BorderPane(console, null, null, controls, null); Scene scene = new Scene(root,600,400); primaryStage.setScene(scene); primaryStage.show(); } private static class MessageProducer implements Runnable { private final BlockingQueue messageQueue ; public MessageProducer(BlockingQueue messageQueue) { this.messageQueue = messageQueue ; } @Override public void run() { long messageCount = 0 ; try { while (true) { final String message = "Message " + (++messageCount); messageQueue.put(message); } } catch (InterruptedException exc) { System.out.println("Message producer interrupted: exiting."); } } } public static void main(String[] args) { launch(args); } }