2014-05-06 6 views
15

Ricevo oggetti dati in momenti casuali ad alta frequenza e devo aggiornare la GUI JavaFX con questi. Tuttavia, non voglio riempire la coda degli eventi javafx con un numero molto elevato di runnables (io uso Platform.RunLater).Throttling aggiornamenti gui javafx

Ho pensato a come implementare al meglio un algoritmo di limitazione.

  • Sarebbe meglio avere un thread separato che GUIUpdater controllare ad esempio una coda di blocco per i nuovi oggetti, e poi dorme per esempio per 30ms e quindi controlla di nuovo, in un loop infinito? In tal caso, una coda di blocco sarebbe la struttura dati ottimale? Si noti che ho solo bisogno dell'ultimo oggetto dati e che blockingQueue è una coda FIFO e non riesco a selezionare solo la voce più recente.
  • Oppure - sarebbe meglio semplicemente aggiornare la GUI con Platform.RunLater se nanoTime-startTime> 30ms? In tal caso, non ho bisogno di un thread separato per eseguire Platform.RunLater-call. Tuttavia, se viene ricevuto un aggiornamento quando non è passato 30 ms e quindi non vengono ricevuti aggiornamenti per un po 'di tempo, l'ultimo aggiornamento non verrà visualizzato nella GUI.

Qualche suggerimento su come progettare un algoritmo di limitazione per JavaFX Platform.RunLater GUI si aggiorna in modo breve ed efficiente?

risposta

14

Questo è l'idioma utilizzato nella classe Task per l'implementazione del metodo updateMessage(...) e altri metodi simili. Esso fornisce una bella, robusta soluzione per evitare di allagare il FX Discussione Applicazione:

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); 
    } 
} 

Il AtomicLong contiene il valore corrente da utilizzare per aggiornare l'etichetta. Il conteggio incrementa e aggiorna continuamente lo AtomicLong, ma pianifica solo una chiamata a Platform.runLater(...) se il suo valore corrente è -1. Lo Platform.runLater(...) aggiorna locon il valore corrente dallo AtomicLong e ribalta lo AtomicLong su -1, a indicare che è pronto per un nuovo aggiornamento.

L'effetto qui è programmare nuove chiamate su Platform.runLater(...) ogni volta che il Thread di applicazione FX è pronto a gestirle. Non c'è un intervallo di tempo codificato che potrebbe richiedere la messa a punto.

+0

Perché AtomicLong non è impostato su "-1" (ad indicare che la GUi è pronta per l'aggiornamento) dopo che l'etichetta è stata impostata nella piattaforma.runlater-runnable? – user3607022

+1

@ user3607022 È necessario recuperare il valore corrente e impostare il valore su '-1' * atomicamente *. Se hai recuperato il valore per la visualizzazione, quindi impostalo su '-1' dopo che è stato visualizzato, è possibile che l'altro thread possa impostare nuovamente il valore tra questi due: cioè 1. Il thread dell'interfaccia utente recupera il valore, 2. aggiornamenti del thread in background poiché il valore è '! = - 1' non è pianificato alcun aggiornamento, 3. ui imposta il valore su -1. In questo scenario, potresti "perdere" quel valore intermedio; se fosse l'ultima cosa che il thread ha fatto, ti mancherà in modo permanente. –

+0

Esiste un modo semplice per descrivere esattamente quali aggiornamenti vengono eliminati? – user3607022