2015-06-16 9 views
6

Ho una domanda su multi-threading e l'associazione di una StringProperty.Errore multi-threading durante il binding a StringProperty

Ho una classe CacheManager, che contiene uno Thread che aggiorna la cache con le modifiche sul server. Ora, voglio notificare all'utente un testo e una percentuale del progresso (che sono uno Label e ProgressBar in JavaFX). Io uso statico pubblico DoubleProperty e StringProperty per questo, che sono definiti nella classe CacheManager. Ho appena legano in questo modo:

progressBar.progressProperty().bind(CacheManager.progress); 
someLabel.textProperty().bind(CacheManager.status); 

Ora, nel thread Updater, aggiorno questi Properties. Con DoubleProperty funziona perfettamente e lo ProgressBar mostra perfettamente i progressi. Tuttavia, l'aggiornamento del Label con lo stato (che è il testo dal StringProperty) genera un errore: java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-9

Ora, la mia domanda è: Perché il lavoro DoubleProperty bene, mentre il StringProperty tiri un errore? Qual è la differenza tra loro che considerano il multi-threading?

Tutte le idee su una riprogettazione sono anche benvenute e qualsiasi aiuto è molto apprezzato!

+0

Hai cercato il motivo "IllegalStateException: Non sul thread dell'applicazione FX"? –

+0

Ho appena gettato l'errore nell'aggiornamento 'Thread', quando provo a modificare il testo per la seconda volta. Quindi su questa riga: 'status.set (" Aggiornamento ... ")' – bashoogzaad

+3

I * bet * è perché la skin di 'ProgressBar' imposta un' InvalidationListener' su 'progressProperty', mentre la skin di' Label' imposta un listener di modifiche su 'textProperty'. Nonostante il comportamento di 'progressBar.progressProperty' sia più conveniente, penso che il comportamento di' Label.textProperty' sia più conciso con il fatto che tutte le modifiche all'interfaccia utente devono aver luogo nel thread dell'interfaccia utente. Usa 'Platform.runLater()' o considera la classe 'javafx.concurrent.Service' di JavaFX, che offre le funzionalità che desideri. –

risposta

10

È errato chiamare il codice che provoca modifiche all'interfaccia utente da una discussione diversa da Thread applicazione FX, indipendentemente dal fatto che emetta o meno un'eccezione. Il kit di strumenti FX fa il possibile per generare un'eccezione se si viola questa regola, ma in alcuni casi l'effetto sulle prestazioni è troppo grande per eseguire il controllo. Se si creano questi collegamenti, eventuali modifiche successive alle proprietà a cui si è associato devono essere eseguite sul Thread di applicazione FX. Vale a dire, se si esegue in un thread in background, si deve modificare le proprietà con un codice simile:

Platform.runLater(() -> CacheManager.progress.set(...)); 

e

Platform.runLater(() -> CacheManager.status.set(...)); 

Dato che probabilmente non si desidera che il codice di servizio di essere legato per JavaFX (tramite la classe Platform), si potrebbe considerare l'utilizzo di ascoltatori al posto di attacchi, e la pianificazione degli aggiornamenti dagli ascoltatori:

CacheManager.progress.addListener((obs, oldValue, newValue) -> 
    Platform.runLater(() -> progressBar.setProgress(newValue.doubleValue()))); 
CacheManager.status.addListener((obs, oldStatus, newStatus) -> 
    Platform.runLater(() -> someLabel.setText(newStatus))); 

Se si sostituiscono i collegamenti con questi listener, è possibile aggiornare le proprietà su qualsiasi thread.

+0

Grazie per questa risposta! Davvero fantastico che mostri due modi per farlo! – bashoogzaad

+2

Questa risposta si riduce fondamentalmente a "non associare le proprietà nell'interfaccia utente"; questo è davvero l'unico modo? Sembra un difetto evidente nella progettazione di JavaFX - se la modifica di una proprietà deve avvenire sul thread dell'interfaccia utente, dovrebbe esserci la responsabilità del codice vincolante di farlo lì; non dovresti modificare il tuo modello di dati sul thread dell'interfaccia utente –

+0

L'approccio che spesso prendo è quello di dividere il modello di dati: quindi avrò spesso un modello di interfaccia utente che aggiorna le proprietà sul thread dell'interfaccia utente e fa riferimento a un servizio o simili. Il modello UI osserva le modifiche nel servizio e propaga tali modifiche alle modifiche alle proprie proprietà, gestendo il threading come parte di tale propagazione. Quindi il controllore può associare la vista alle proprietà del modello dell'interfaccia utente. Vedi http://www.oracle.com/technetwork/articles/java/javafxinteg-2062777.html per alcune tecniche simili. –