Hi All,
I am trying to Update a Label from a task by using bindings.
However When I "Bind" the label's text property with a String property from the Task, Illegal State exception is thrown. Saying its not on the JavaFX thread.
The Exception occurs whenever I try to set the String property from withing the Task.
Please do not suggest to use platform.RunLater(). I want to do it through bindings as the values I am trying to display in the label ( later on ) might change too frequently and i don't want to flood the UI thread's queue with runnable objects.
Please let me know what I am doing wrong and what I have to change to make it work properly with bindings. ( I am new to bindings and the JavaFx Concurrency API )
Below is my Code.
public class MyTask extends Task<String>{
MyTask(){
System.out.println("Task Constructor on Thread "+Thread.currentThread().getName());
}
private StringProperty myStringProperty = new SimpleStringProperty(){
{
System.out.println("Creating stringProperty on Thread "+Thread.currentThread().getName());
}
};
private final void setFileString(String value) {
System.out.println("Setting On Thread"+Thread.currentThread().getName());
myStringProperty.set(value); }
public final String getFileString() { return myStringProperty.get(); }
public final StringProperty fileStringProperty() {
System.out.println("Fetching property On Thread"+Thread.currentThread().getName());
return myStringProperty; }
@Override
public String call() throws Exception{
System.out.println("Task Called on thread "+Thread.currentThread().getName());
for(int counter=0;counter<100;counter++){
try{
setFileString(""+counter);
}catch(Exception e){
e.printStackTrace();
}
Thread.sleep(100);
System.out.println("Counter "+counter);
}
return "COMPLETED";
}
}
public class MyService extends Service<String> {
MyTask myTask;
public MyService(){
System.out.println("Service Constructor on Thread "+Thread.currentThread().getName());
myTask=new MyTask();
}
@Override
public Task createTask(){
System.out.println("Creating task on Thread "+Thread.currentThread().getName());
return myTask;
}
}
public class ServiceAndTaskExperiment extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
public class SampleController implements Initializable {
@FXML
private Label label;
@FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
myTestService.start(); //This will throw out exceptions when the button is clicked again, it does not matter
}
MyService myTestService=new MyService();
@Override
public void initialize(URL url, ResourceBundle rb) {
label.setText("Hello World!");
//adding the below Line causes the exception
label.textProperty().bind(myTestService.myTask.fileStringProperty()); //removing this line removes the exception, ofcourse the label wont update.
}
}
//sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml" fx:controller="serviceandtaskexperiment.SampleController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
And this is the output with the bindings on:
Output: when binding is enabled label.textProperty().bind(myTestService.myTask.fileStringProperty());
Service Constructor on Thread JavaFX Application Thread
Creating stringProperty on Thread JavaFX Application Thread
Task Constructor on Thread JavaFX Application Thread
Fetching property On ThreadJavaFX Application Thread
You clicked me!
Creating task on Thread JavaFX Application Thread
Task Called on threadThread-4
Setting On ThreadThread-4
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:398)
at javafx.scene.Parent$1.onProposedChange(Parent.java:245)
at com.sun.javafx.collections.VetoableObservableList.setAll(VetoableObservableList.java:90)
at com.sun.javafx.collections.ObservableListWrapper.setAll(ObservableListWrapper.java:314)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:602)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:209)
at com.sun.javafx.scene.control.skin.SkinBase$3.changed(SkinBase.java:282)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:107)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
at javafx.beans.property.StringPropertyBase.access$100(StringPropertyBase.java:67)
at javafx.beans.property.StringPropertyBase$Listener.invalidated(StringPropertyBase.java:236)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at serviceandtaskexperiment.MyTask.setFileString(MyTask.java:24)
at serviceandtaskexperiment.MyTask.call(MyTask.java:36)
at serviceandtaskexperiment.MyTask.call(MyTask.java:11)
at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Output with the bindings Removed: ( label wont update )
Service Constructor on Thread JavaFX Application Thread
Creating stringProperty on Thread JavaFX Application Thread
Task Constructor on Thread JavaFX Application Thread
You clicked me!
Creating task on Thread JavaFX Application Thread
Task Called on threadThread-4
Setting On ThreadThread-4
Counter 0
Setting On ThreadThread-4
Counter 1
Setting On ThreadThread-4
Counter 2
Setting On ThreadThread-4