Reputation: 107
For a school project, I am programming a little Visualization for a PLC. Therefore I have a mySQL Database which has all the Information. Currently, when I click on a Button the program connects to the Database and gets it Information in an ArrayList. Then it checks the information in the ArrayList and puts Data in a ListView.
The problem is, that I want to let the program do that in a Service. As I said the GUI depends on the ArrayList. And I can't change the GUI in the service because then this Exception appears
Sep 02, 2016 9:19:02 PM javafx.concurrent.Service lambda$static$488
WARNING: Uncaught throwable in javafx concurrent thread pool
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(Unknown Source)
at javafx.scene.Parent$2.onProposedChange(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(Unknown Source)
at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(Unknown Source)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(Unknown Source)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(Unknown Source)
at javafx.beans.value.WeakChangeListener.changed(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(Unknown Source)
at javafx.beans.property.StringPropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.StringPropertyBase.set(Unknown Source)
at javafx.beans.property.StringPropertyBase.set(Unknown Source)
at javafx.beans.property.StringProperty.setValue(Unknown Source)
at javafx.scene.control.Labeled.setText(Unknown Source)
at application.Controller$1$1.call(Controller.java:290)
at application.Controller$1$1.call(Controller.java:1)
at javafx.concurrent.Task$TaskCallable.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javafx.concurrent.Service.lambda$null$493(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javafx.concurrent.Service.lambda$executeTask$494(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
My first Idea was to extract only the SQL stuff in the Service and then get the Data via the Method
service.getValue();
But the problem with this is that I don't know when the Service is finished to get the Data, because currently, the data is null.
Upvotes: 5
Views: 7652
Reputation: 399
Basically, you want to run some process and return some value,
Of course Service
nails it. This is how I go about my case and it works perfectly.
Service process = new Service() {
@Override
protected Task createTask() {
return new Task() {
@Override
protected ObjectX call() throws Exception {
updateMessage("Some message that may change with execution");
updateProgress( workDone, totalWork );
return ObjectX;
}
};
}
};
process.setOnSucceeded( e -> {
ObjectX processValue = (ObjectX)process.getValue();
// TODO, . . .
// You can modify any GUI element from here...
// ...with the values you got from the service
});
process.start();
THINGS TO NOTE
protected ObjectX call()
can return any type of object. Just make sure its an object and not a primitive type. You can go as far as populate some GUI elements inside this process and return it as an object, eg. protected VBox call()
. . . . return my_vbox;
ObjectX processValue = (ObjectX)processList.getValue();
=> You should cast the value you got from the Service
back to the Object you want to use.
If is just Object
, you may not have to. But I doubt if you will ever have to use just Object
.processList.setOnFailed()
, processList.setOnRunning()
, processList.setOnCancelled()
, processList.setOnScheduled()
,You can also bind some GUI elements to some Thread properties like this
label.textProperty.bind( process.messageProperty ); // messageProperty is a StringProperty
progressBar.progressProperty.bind( process.progressProperty )
make sure all the methods to further enhance your process have been created and initiated before calling the process.start()
; Nothing happens until you have started the process.
I hope this helps
Upvotes: 8
Reputation: 7255
The Service
class provides methods so you know if it has been cancelled,succeded or failed.
In the Constructor of your Class which extends Service use these methods :
// succeeded?
this.setOnSucceeded(s -> {
// ...
});
// failed
this.setOnFailed(f -> {
// ...
});
// cancelled?
this.setOnCancelled(c -> {
// ...
});
Also remember to use updateProgress(current,maximum)
; so you know what is happening during it's execution.
Here is a full example:
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class ExampleService extends Service<Boolean> {
/**
* Constructor
*/
public ExampleService() {
// succeeded?
this.setOnSucceeded(s -> {
// ...
});
// failed
this.setOnFailed(f -> {
// ...
});
// cancelled?
this.setOnCancelled(c -> {
// ...
});
}
@Override
protected Task<Boolean> createTask() {
return new Task<Boolean>(){
@Override
protected Boolean call() throws Exception {
boolean result = false;
//your code
//.......
//updateProgress(current,total)
return result;
}
};
}
}
Upvotes: 2