2014-05-06 13 views
15

Ich empfange Datenobjekte zu zufälligen Zeiten mit einer hohen Frequenz, und müssen die JavaFX GUI mit diesen aktualisieren. Ich möchte jedoch die javafx Event Queue nicht mit einer sehr großen Anzahl von Runnables füllen (ich benutze Platform.RunLater).Drosselung javafx gui Updates

Ich habe darüber nachgedacht, wie man eine Drosselung Algorithmus am besten umsetzen.

  • Wäre es am besten, einen separaten GUIUpdater-Thread zu haben, der zum Beispiel eine blockierende Warteschlange auf neue Objekte prüft und dann zum Beispiel für 30ms schläft und dann erneut prüft, in einer Endlosschleife? Wäre in diesem Fall eine blockierende Warteschlange die optimale Datenstruktur? Bitte beachten Sie, dass ich nur das letzte Datenobjekt benötige und die blockingQueue eine FIFO-Warteschlange ist und ich nicht nur den letzten Eintrag auswählen kann.
  • Oder - wäre es besser, einfach die GUI mit Platform.RunLater zu aktualisieren, wenn nanoTime-startTime> 30ms? In diesem Fall brauche ich keinen separaten Thread, um den Platform.RunLater-Aufruf auszuführen. Wenn jedoch ein Update empfangen wird, wenn 30 ms nicht verstrichen sind und dann für einige Zeit keine Updates empfangen werden, wird das letzte Update nicht in der GUI angezeigt.
  • Alle Vorschläge, wie man einen Drosselungsalgorithmus für JavaFX Platform.RunLater GUI Updates auf eine kurze, effiziente Weise entwickelt?

    Antwort

    14

    Dies ist das in der Klasse Task verwendete Idiom zur Implementierung der updateMessage(...)-Methode und anderer ähnlicher Methoden. Es bietet eine schöne, robuste Lösung, die den FX Anwendung Faden zu vermeiden Überschwemmungen:

    import java.util.concurrent.atomic.AtomicLong; 
    
    import javafx.application.Application; 
    import javafx.application.Platform; 
    import javafx.geometry.Insets; 
    import javafx.geometry.Pos; 
    import javafx.scene.Scene; 
    import javafx.scene.control.Label; 
    import javafx.scene.layout.VBox; 
    import javafx.stage.Stage; 
    
    public class ThrottlingCounter extends Application { 
    
        @Override 
        public void start(Stage primaryStage) { 
         final AtomicLong counter = new AtomicLong(-1); 
         final Label label = new Label(); 
         final Thread countThread = new Thread(new Runnable() { 
          @Override 
          public void run() { 
           long count = 0 ; 
           while (true) { 
            count++ ; 
            if (counter.getAndSet(count) == -1) { 
             updateUI(counter, label); 
            } 
           } 
          } 
         }); 
         countThread.setDaemon(true); 
         countThread.start(); 
    
         VBox root = new VBox(); 
         root.getChildren().add(label); 
         root.setPadding(new Insets(5)); 
         root.setAlignment(Pos.CENTER); 
    
         Scene scene = new Scene(root, 150, 100); 
         primaryStage.setScene(scene); 
         primaryStage.show(); 
        } 
    
        private void updateUI(final AtomicLong counter, 
          final Label label) { 
         Platform.runLater(new Runnable() { 
          @Override 
          public void run() { 
           final String msg = String.format("Count: %,d", counter.getAndSet(-1)); 
           label.setText(msg); 
          } 
         }); 
        } 
    
        public static void main(String[] args) { 
         launch(args); 
        } 
    } 
    

    Die AtomicLong hält den aktuellen Wert verwendet werden, um das Etikett zu aktualisieren. Die Zählung erhöht und aktualisiert die AtomicLong fortlaufend, plant jedoch nur einen Anruf an Platform.runLater(...), wenn der aktuelle Wert -1 ist. Die Platform.runLater(...) aktualisiert die Label mit dem aktuellen Wert aus den AtomicLong und dreht die AtomicLong auf -1 zurück, was darauf hinweist, dass es für ein neues Update bereit ist.

    Die hier Wirkung ist es, neue Anrufe zu Platform.runLater(...) planen, wenn der FX Anwendung Faden bereit ist, sie zu handhaben. Es gibt kein fest codiertes Zeitintervall, das optimiert werden müsste.

    +0

    Warum nicht die Atomic wird auf „-1“ (was die gui ist bereit, zu aktualisieren), nachdem das Etikett in der platform.runlater-runnable eingestellt wurde? – user3607022

    +1

    @ user3607022 Sie müssen den aktuellen Wert abrufen und den Wert atomisch auf "-1" * setzen. Wenn Sie den Wert für die Anzeige abgerufen haben und ihn nach der Anzeige auf "-1" setzen, ist es möglich, dass der andere Thread den Wert erneut zwischen diesen beiden Werten festlegt: 1. Der UI-Thread ruft den Wert ab. 2. Hintergrund-Thread-Aktualisierungen Da der Wert '! = - 1 'ist, wird keine Aktualisierung geplant, 3. ui setzt den Wert auf -1. In diesem Szenario könnten Sie diesen Zwischenwert "verpassen"; Wenn es das Letzte wäre, was der Thread getan hätte, würden Sie ihn dauerhaft vermissen. –

    +0

    Gibt es eine einfache Möglichkeit, genau zu beschreiben, welche Updates gelöscht werden? – user3607022