Moin zusammen !
Ich bin auf der Suche nach einer BestPractice Möglichkeit für die Umsetzung. Folgende Gegebenheiten:
1. Es gibt einen potentiell langlaufenden Prozess mit sehr vielen Statusmeldungen.
2. Alle Statusmeldungen MÜSSEN in der GUI ausgegeben werden (fortlaufend in einer TextArea)
3. Der Thread soll nicht durch beliebige Thread.sleep() Anweisungen künstlich verlangsamt werden sondern maximal soweit ausgebremst werden das die Updates der GUI erfolgen können.
Da es ein langlaufender Prozess ist verbietet sich schon mal das Handling im JavaFX Application Thread, somit wird hier ein separater Thread erstellt.
Das updaten der GUI über Platform.RunLater() bringt nicht den gewünschten Erfolg. Es werden zwar alle Updates ausgegeben, da die Updates aber teils sehr schnell erfolgen wird der FX Applikation Thread somit bombardiert und fängt an zu haken.
Ein Update über die JavaFX internen Property Bindings sorgt für einen sehr schnellen Ablauf, allerdings werden nicht alle Updates ausgegeben. Ich bin mir nicht sicher aber ich vermute mal das das mit der Target FPS von JavaFX zusammenhängt (ca 63 FPS ? ) und er die Bindings an den Properties der Nodes demnach nur alle ca. 16ms ausliest ?
Ich habe nun eine funktionierende Lösung entwickelt die ich hier gerne zur Diskussion stellen würde. Die Idee an sich stammt aus einem stackoverflow() Beitrag, habe dies dann an meine Bedürfnisse angepasst. Will mich nicht mit fremden Federn schmücken ;-)
Im ViewController wird eine BlockingQueue instanziert.
Bei der Erstellung des Task definiere ich auch einen AnimationTimer. Diesen starte ich sobald der Task auf schedulded gesetzt wird. Bei jedem Durchlauf holt er sich ein Runnable updateRequest aus der BlockingQueue und führt diesen aus. Ist die Queue leer prüft er ob der Task noch läuft. Ist der Task beendet so beendet sich der AnimationTimer selbst.
Der Task selbst hält eine Referenz auf den ViewController und gibt die Updates über Aufruf dieser Methode des ViewControllers in die BlockingQueue.
Der Task wird somit auf die FPS verlangsamt, da er jedes malwenn er versucht die BlockingQueue zu füllen ausgebremst wird wenn diese 10 Elemente beinhaltet. Für den Fall das ein einzelnes Update mal länger dauern sollte als der Durchlauf des AnimationTimers hat die BlockingQueue eine Größe von 10, so das der AnimationTimer i.d.R. immer Updates finden sollte während der Task läuft.
Ich bin auf der Suche nach einer BestPractice Möglichkeit für die Umsetzung. Folgende Gegebenheiten:
1. Es gibt einen potentiell langlaufenden Prozess mit sehr vielen Statusmeldungen.
2. Alle Statusmeldungen MÜSSEN in der GUI ausgegeben werden (fortlaufend in einer TextArea)
3. Der Thread soll nicht durch beliebige Thread.sleep() Anweisungen künstlich verlangsamt werden sondern maximal soweit ausgebremst werden das die Updates der GUI erfolgen können.
Da es ein langlaufender Prozess ist verbietet sich schon mal das Handling im JavaFX Application Thread, somit wird hier ein separater Thread erstellt.
Das updaten der GUI über Platform.RunLater() bringt nicht den gewünschten Erfolg. Es werden zwar alle Updates ausgegeben, da die Updates aber teils sehr schnell erfolgen wird der FX Applikation Thread somit bombardiert und fängt an zu haken.
Ein Update über die JavaFX internen Property Bindings sorgt für einen sehr schnellen Ablauf, allerdings werden nicht alle Updates ausgegeben. Ich bin mir nicht sicher aber ich vermute mal das das mit der Target FPS von JavaFX zusammenhängt (ca 63 FPS ? ) und er die Bindings an den Properties der Nodes demnach nur alle ca. 16ms ausliest ?
Ich habe nun eine funktionierende Lösung entwickelt die ich hier gerne zur Diskussion stellen würde. Die Idee an sich stammt aus einem stackoverflow() Beitrag, habe dies dann an meine Bedürfnisse angepasst. Will mich nicht mit fremden Federn schmücken ;-)
Im ViewController wird eine BlockingQueue instanziert.
Java:
BlockingQueue<Runnable> updateUIQueue = new ArrayBlockingQueue<>(10);
Bei der Erstellung des Task definiere ich auch einen AnimationTimer. Diesen starte ich sobald der Task auf schedulded gesetzt wird. Bei jedem Durchlauf holt er sich ein Runnable updateRequest aus der BlockingQueue und führt diesen aus. Ist die Queue leer prüft er ob der Task noch läuft. Ist der Task beendet so beendet sich der AnimationTimer selbst.
Code:
private Task getCreateBillingDataTask() {
Task<Void> task = new SampleTask();
AnimationTimer updateUITimer = new AnimationTimer() {
@Override
public void handle(long now) {
Runnable updateRequest = updateUIQueue.poll();
if (updateRequest != null) {
updateRequest.run();
} else if (!task.isRunning()) {
this.stop();
}
}
};
task.setOnScheduled(e -> updateUITimer.start());
return task;
}
Der Task selbst hält eine Referenz auf den ViewController und gibt die Updates über Aufruf dieser Methode des ViewControllers in die BlockingQueue.
Code:
public void createMessageUpdateRequest(String message){
Runnable updateRequest = () -> taMessages.appendText(message);
try {
updateUIQueue.put(updateRequest);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Der Task wird somit auf die FPS verlangsamt, da er jedes malwenn er versucht die BlockingQueue zu füllen ausgebremst wird wenn diese 10 Elemente beinhaltet. Für den Fall das ein einzelnes Update mal länger dauern sollte als der Durchlauf des AnimationTimers hat die BlockingQueue eine Größe von 10, so das der AnimationTimer i.d.R. immer Updates finden sollte während der Task läuft.