You are on page 1of 23

Tutoriel sur l'excution d'une

tche de fond en JavaFX


Comment lancer un traitement lourd en arrire-plan pour ne pas bloquer son UI JavaFX.
Par Fabrice Bouy

Date de publication : 18 mars 2015


Dernire mise jour : 24 mars 2015

Cet article a pour but de vous expliquer comment excuter une tche de fond en JavaFX
sans avoir recourir un Thread lance manuellement et sans bloquer l'excution du thread
principal de gestion des vnements.
Pour ragir au contenu de cet article, un espace de dialogue vous est propos sur le forum
Commentez

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

I - Introduction..............................................................................................................................................................3
II - Problmatique........................................................................................................................................................ 3
II-A - Exemple.........................................................................................................................................................3
II-B - Raisonnement............................................................................................................................................... 6
II-C - Solution......................................................................................................................................................... 6
III - Interfaces et classes importantes....................................................................................................................... 10
III-A - javafx.concurrent.Worker<V>..................................................................................................................... 10
III-B - javafx.concurrent.Service<V>.....................................................................................................................10
III-C - javafx.concurrent.Task<V>......................................................................................................................... 11
III-D - Javafx.concurrent.ScheduledService<V>...................................................................................................11
IV - Utiliser un service............................................................................................................................................... 12
IV-A - Cration et lancement............................................................................................................................... 12
IV-B - Annulation.................................................................................................................................................. 12
IV-C - Relancer un service...................................................................................................................................13
IV-D - Avancement du service............................................................................................................................. 13
IV-E - tat du service...........................................................................................................................................13
IV-E-1 - ChangeListener sur l'tat du service................................................................................................ 14
IV-E-2 - Callback sur le service......................................................................................................................15
IV-F - Rcuprer le rsultat................................................................................................................................. 16
IV-G - Rcuprer la raison de l'chec................................................................................................................. 16
V - Utiliser une tche.................................................................................................................................................17
V-A - Notification de l'avancement.......................................................................................................................17
V-B - Annulation................................................................................................................................................... 17
V-B-1 - Lors d'un appel bloquant................................................................................................................... 18
V-C - tat d'une tche......................................................................................................................................... 18
V-D - Produire des rsultats partiels....................................................................................................................19
V-D-1 - JDK 7................................................................................................................................................. 19
V-D-2 - JDK 8................................................................................................................................................. 20
V-E - Utiliser une tche dans un contexte non JavaFX...................................................................................... 21
VI - Utiliser un service rptable...............................................................................................................................21
VI-A - Dlai avant l'excution...............................................................................................................................21
VI-B - Priode d'excution................................................................................................................................... 22
VI-C - Gestion des checs...................................................................................................................................22
VI-D - Derniers bons rsultats............................................................................................................................. 22
VII - Conclusion......................................................................................................................................................... 22
VIII - Remerciements................................................................................................................................................. 22
IX - Liens....................................................................................................................................................................23

-2-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

I - Introduction
Comme dans n'importe quel logiciel, il peut arriver que l'utilisateur soit amen excuter une tche de longue dure
pour par exemple un calcul, charger ou sauvegarder un fichier, effectuer une requte web ou sur une base de donnes
ou encore faire une pause pendant une dure dtermine ou effectuer une boucle infinie pour surveiller l'tat d'un
port, d'un priphrique ou d'un fichier.
Or si on ne prend pas attention la manire dont ces tches longues sont traites, il est trs facile de bloquer le
fonctionnement de l'UI, ce qui laissera penser l'utilisateur que le programme a plant puisque plus rien ne rpond
ses saisies. Fort heureusement, l'API JavaFX fournit via son API de concurrence une interface et une paire de
classes tout spcialement ddies la gestion des tches de fond, ce qui facilite grandement la gestion de ce genre
d'oprations.

II - Problmatique
II-A - Exemple
Prenons un problme simple : nous disposons d'une UI contenant un simple bouton et nous allons lancer le calcul
de 1 000 000 d'entiers lorsque nous cliquons sur ce bouton. Nous allons galement changer le curseur de l'UI et
le remplacer par un curseur d'attente et nous dsactiverons les boutons permettant de lancer le calcul pendant que
ce dernier tourne.
Code JDK7

1. package test;
2.
3. import javafx.application.Application;
4. import javafx.application.Platform;
5. import javafx.event.ActionEvent;
6. import javafx.event.EventHandler;
7. import javafx.scene.Cursor;
8. import javafx.scene.Scene;
9. import javafx.scene.control.Button;
10. import javafx.scene.control.Menu;
11. import javafx.scene.control.MenuBar;
12. import javafx.scene.control.MenuItem;
13. import javafx.scene.layout.BorderPane;
14. import javafx.scene.layout.StackPane;
15. import javafx.stage.Stage;
16.
17. public class Main extends Application {
18.
19.
private MenuItem calculateItem;
20.
private Button calculateButton;
21.
private Scene scene;
22.
23.
@Override
24.
public void start(Stage primaryStage) {
25.
final MenuItem exitItem = new MenuItem("Quitter");
26.
exitItem.setOnAction(new EventHandler<ActionEvent>() {
27.
28.
@Override
29.
public void handle(ActionEvent t) {
30.
Platform.exit();
31.
}
32.
});
final Menu fileMenu = new Menu("Fichier");
33.
fileMenu.getItems().add(exitItem);
34.
calculateItem = new MenuItem("Calculer");
35.
calculateItem.setOnAction(new EventHandler<ActionEvent>() {
36.
37.
@Override
38.
public void handle(ActionEvent t) {
39.
doCalculate();
40.
}
-3-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK7
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83. }

});
final Menu actionMenu = new Menu("Action");
actionMenu.getItems().add(calculateItem);
final MenuBar menuBar = new MenuBar();
menuBar.getMenus().setAll(fileMenu, actionMenu);
calculateButton = new Button();
calculateButton.setText("Lancer le calcul !");
calculateButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
doCalculate();
}

});
StackPane center = new StackPane();
center.getChildren().add(calculateButton);
final BorderPane root = new BorderPane();
root.setTop(menuBar);
root.setCenter(center);
scene = new Scene(root, 300, 250);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();

private void doCalculate() {


final Cursor oldCursor = scene.getCursor();
scene.setCursor(Cursor.WAIT);
calculateItem.setDisable(true);
calculateButton.setDisable(true);
final int maxIterations = 1000000;
for (int iterations = 0; iterations < maxIterations; iterations ++) {
System.out.println(iterations);
}
scene.setCursor(oldCursor);
calculateItem.setDisable(false);
calculateButton.setDisable(false);
}
public static void main(String[] args) {
launch(args);
}

Code JDK8

1. package test;
2.
3. import javafx.application.Application;
4. import javafx.application.Platform;
5. import javafx.event.ActionEvent;
6. import javafx.scene.Cursor;
7. import javafx.scene.Scene;
8. import javafx.scene.control.Button;
9. import javafx.scene.control.Menu;
10. import javafx.scene.control.MenuBar;
11. import javafx.scene.control.MenuItem;
12. import javafx.scene.layout.BorderPane;
13. import javafx.scene.layout.StackPane;
14. import javafx.stage.Stage;
15.
16. public class Main extends Application {
17.
18.
private MenuItem calculateItem;
19.
private Button calculateButton;
20.
private Scene scene;
21.
22.
@Override
23.
public void start(Stage primaryStage) {
24.
final MenuItem exitItem = new MenuItem("Quitter");
25.
exitItem.setOnAction((ActionEvent t) -> Platform.exit());
-4-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK8
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65. }

final Menu fileMenu = new Menu("Fichier");


fileMenu.getItems().add(exitItem);
calculateItem = new MenuItem("Calculer");
calculateItem.setOnAction((ActionEvent t) -> doCalculate());
final Menu actionMenu = new Menu("Action");
actionMenu.getItems().add(calculateItem);
final MenuBar menuBar = new MenuBar();
menuBar.getMenus().setAll(fileMenu, actionMenu);
calculateButton = new Button();
calculateButton.setText("Lancer le calcul !");
calculateButton.setOnAction((ActionEvent event) -> doCalculate());
StackPane center = new StackPane();
center.getChildren().add(calculateButton);
final BorderPane root = new BorderPane();
root.setTop(menuBar);
root.setCenter(center);
scene = new Scene(root, 300, 250);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();

private void doCalculate() {


final Cursor oldCursor = scene.getCursor();
scene.setCursor(Cursor.WAIT);
calculateItem.setDisable(true);
calculateButton.setDisable(true);
final int maxIterations = 1000000;
for (int iterations = 0; iterations < maxIterations; iterations ++) {
System.out.println(iterations);
}
scene.setCursor(oldCursor);
calculateItem.setDisable(false);
calculateButton.setDisable(false);
}
public static void main(String[] args) {
launch(args);
}

Ce qui nous donne une interface similaire celle-ci :

Figure 1 - L'interface qui va lancer le calcul.

-5-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Nous pouvons dsormais lancer notre calcul soit en cliquant directement sur le bouton Lancer le calcul !, soit en
allant dans le menu Action -> Calculer.
Lorsque nous dmarrons le calcul, nous pouvons nous rendre compte de plusieurs choses :

le curseur ne change jamais d'apparence ;


le bouton n'est jamais dsactiv ;
l'interface est compltement bloque : il n'est plus possible d'aller dans les menus par exemple ou mme de
fermer la fentre normalement.

C'est bien normal : nous ragissons immdiatement l'action sur le bouton ou le menu et nous excutons notre long
calcul dans le mme thread que celui qui gre l'affichage et les vnements dans l'UI. Cette dernire ne peut donc
plus rafraichir son affichage ni mme ragir nos saisies.
Et souvenez-vous qu'il s'agit ici d'un simple calcul numrique, le problme se pose galement lors de la lecture ou
l'criture de fichiers, d'un accs une base de donnes, sur le rseau ou sur Internet

II-B - Raisonnement
Tout comme Swing et AWT s'excutent dans l'EDT (Event Dispatch Thread), JavaFX utilise des threads qui lui sont
propres tels que le JavaFX Application Thread, le thread de rendu Prism ou encore le thread media. Les vnements
reus via les listeners ou le binding ou encore les appels aux callbacks sont tous excuts dans le JavaFX Application
Thread.
Tout comme dans Swing et AWT, lancer un traitement long dans le thread de gestion des vnements de JavaFX
peut mener un blocage (freeze) de l'UI de l'application qui ne rpond alors plus aux entres de l'utilisateur. Ce
dernier peut alors tre amen penser que l'application est bogue ou plante. Il faut donc excuter ce genre de
traitement en tche de fond (background task) sans bloquer les threads de l'UI.
Bien qu'il soit possible de lancer manuellement un ou plusieurs Thread pour effectuer le traitement, la gestion de la
remonte d'informations vers l'UI (progression, message, remonte des erreurs) peut s'avrer assez complique
programmer. C'est ici qu'intervient l'API de concurrence de JavaFX !

II-C - Solution
Reprenons notre code et utilisons l'API de concurrence de JavaFX. Pour cela, nous allons juste modifier le code
de la mthode doCalculate() ainsi que les imports pour lister les nouvelles classes ncessaires. Nous reviendrons
ultrieurement sur les diverses classes et mcanismes introduits dans ce code :
Code JDK7

1. package test;
2.
3. import javafx.application.Application;
4. import javafx.application.Platform;
5. import javafx.beans.value.ChangeListener;
6. import javafx.beans.value.ObservableValue;
7. import javafx.concurrent.Service;
8. import javafx.concurrent.Task;
9. import javafx.concurrent.Worker;
10. import javafx.event.ActionEvent;
11. import javafx.event.EventHandler;
12. import javafx.scene.Cursor;
13. import javafx.scene.Scene;
14. import javafx.scene.control.Button;
15. import javafx.scene.control.Menu;
16. import javafx.scene.control.MenuBar;
17. import javafx.scene.control.MenuItem;
18. import javafx.scene.layout.BorderPane;
19. import javafx.scene.layout.StackPane;
-6-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK7

20. import javafx.stage.Stage;


21.
22. public class Main extends Application {
23.
24.
private MenuItem calculateItem;
25.
private Button calculateButton;
26.
private Scene scene;
27.
28.
@Override
29.
public void start(Stage primaryStage) {
30.
final MenuItem exitItem = new MenuItem("Quitter");
31.
exitItem.setOnAction(new EventHandler<ActionEvent>() {
32.
33.
@Override
34.
public void handle(ActionEvent t) {
35.
Platform.exit();
36.
}
37.
});
final Menu fileMenu = new Menu("Fichier");
38.
fileMenu.getItems().add(exitItem);
39.
calculateItem = new MenuItem("Calculer");
40.
calculateItem.setOnAction(new EventHandler<ActionEvent>() {
41.
42.
@Override
43.
public void handle(ActionEvent t) {
44.
doCalculate();
45.
}
46.
});
47.
final Menu actionMenu = new Menu("Action");
48.
actionMenu.getItems().add(calculateItem);
49.
final MenuBar menuBar = new MenuBar();
50.
menuBar.getMenus().setAll(fileMenu, actionMenu);
51.
calculateButton = new Button();
52.
calculateButton.setText("Lancer le calcul !");
53.
calculateButton.setOnAction(new EventHandler<ActionEvent>() {
54.
55.
@Override
56.
public void handle(ActionEvent event) {
57.
doCalculate();
58.
}
59.
});
60.
StackPane center = new StackPane();
61.
center.getChildren().add(calculateButton);
62.
final BorderPane root = new BorderPane();
63.
root.setTop(menuBar);
64.
root.setCenter(center);
65.
scene = new Scene(root, 300, 250);
66.
primaryStage.setTitle("Test");
67.
primaryStage.setScene(scene);
68.
primaryStage.show();
69.
}
70.
71.
private void doCalculate() {
72.
final Cursor oldCursor = scene.getCursor();
73.
scene.setCursor(Cursor.WAIT);
74.
calculateItem.setDisable(true);
75.
calculateButton.setDisable(true);
76.
final Service<Void> calculateService = new Service<Void>() {
77.
78.
@Override
79.
protected Task<Void> createTask() {
80.
return new Task<Void>() {
81.
82.
@Override
83.
protected Void call() throws Exception {
84.
final int maxIterations = 1000000;
85.
for (int iterations = 0; iterations < maxIterations; iterations ++) {
86.
System.out.println(iterations);
87.
}
88.
return null;
89.
}
-7-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK7

90.
};
91.
}
92.
};
93.
calculateService.stateProperty().addListener(new ChangeListener<Worker.State>() {
94.
95.
@Override
96.
public void changed(ObservableValue<? extends Worker.State> observableValue,
Worker.State oldValue, Worker.State newValue) {
97.
switch (newValue) {
98.
case FAILED:
99.
case CANCELLED:
100.
case SUCCEEDED:
101.
scene.setCursor(oldCursor);
102.
calculateItem.setDisable(false);
103.
calculateButton.setDisable(false);
104.
break;
105.
}
106.
}
107.
});
108.
calculateService.start();
109.
}
110.
111.
public static void main(String[] args) {
112.
launch(args);
113.
}
114. }

Code JDK

1. package test;
2.
3. import javafx.application.Application;
4. import javafx.application.Platform;
5. import javafx.beans.value.ObservableValue;
6. import javafx.concurrent.Service;
7. import javafx.concurrent.Task;
8. import javafx.concurrent.Worker;
9. import javafx.event.ActionEvent;
10. import javafx.scene.Cursor;
11. import javafx.scene.Scene;
12. import javafx.scene.control.Button;
13. import javafx.scene.control.Menu;
14. import javafx.scene.control.MenuBar;
15. import javafx.scene.control.MenuItem;
16. import javafx.scene.layout.BorderPane;
17. import javafx.scene.layout.StackPane;
18. import javafx.stage.Stage;
19.
20. public class Main extends Application {
21.
22.
private MenuItem calculateItem;
23.
private Button calculateButton;
24.
private Scene scene;
25.
26.
@Override
27.
public void start(Stage primaryStage) {
28.
final MenuItem exitItem = new MenuItem("Quitter");
29.
exitItem.setOnAction((ActionEvent t) -> Platform.exit());
30.
final Menu fileMenu = new Menu("Fichier");
31.
fileMenu.getItems().add(exitItem);
32.
calculateItem = new MenuItem("Calculer");
33.
calculateItem.setOnAction((ActionEvent t) -> doCalculate());
34.
final Menu actionMenu = new Menu("Action");
35.
actionMenu.getItems().add(calculateItem);
36.
final MenuBar menuBar = new MenuBar();
37.
menuBar.getMenus().setAll(fileMenu, actionMenu);
38.
calculateButton = new Button();
39.
calculateButton.setText("Lancer le calcul !");
40.
calculateButton.setOnAction((ActionEvent event) -> doCalculate());
41.
StackPane center = new StackPane();
42.
center.getChildren().add(calculateButton);
-8-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK

43.
final BorderPane root = new BorderPane();
44.
root.setTop(menuBar);
45.
root.setCenter(center);
46.
scene = new Scene(root, 300, 250);
47.
primaryStage.setTitle("Test");
48.
primaryStage.setScene(scene);
49.
primaryStage.show();
50.
}
51.
52.
private void doCalculate() {
53.
final Cursor oldCursor = scene.getCursor();
54.
scene.setCursor(Cursor.WAIT);
55.
calculateItem.setDisable(true);
56.
calculateButton.setDisable(true);
57.
final Service<Void> calculateService = new Service<Void>() {
58.
59.
@Override
60.
protected Task<Void> createTask() {
61.
return new Task<Void>() {
62.
63.
@Override
64.
protected Void call() throws Exception {
65.
final int maxIterations = 1000000;
66.
for (int iterations = 0; iterations < maxIterations; iterations ++) {
67.
System.out.println(iterations);
68.
}
69.
return null;
70.
}
71.
};
72.
}
73.
};
74.
calculateService.stateProperty().addListener((ObservableValue<? extends Worker.State>
observableValue, Worker.State oldValue, Worker.State newValue) -> {
75.
switch (newValue) {
76.
case FAILED:
77.
case CANCELLED:
78.
case SUCCEEDED:
79.
scene.setCursor(oldCursor);
80.
calculateItem.setDisable(false);
81.
calculateButton.setDisable(false);
82.
break;
83.
}
84.
});
85.
calculateService.start();
86.
}
87.
88.
public static void main(String[] args) {
89.
launch(args);
90.
}
91. }

Dsormais, notre programme se comporte comme prvu :

le curseur change d'apparence au dbut et la fin du calcul ;


le bouton, ainsi que l'entre dans le menu, est dsactiv au dbut du calcul et ractiv lorsque ce dernier se
termine ;
l'interface n'est plus bloque : nous pouvons naviguer dans les menus et nous pouvons fermer la fentre
normalement.

Nous pouvons galement voir que nous n'avons pas eu crer un nouveau thread manuellement ni d'avoir
nous synchroniser dessus. Cependant le calcul s'est bien droul dans un autre fil d'excution diffrent du JavaFX
Application Thread.

-9-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

III - Interfaces et classes importantes


III-A - javafx.concurrent.Worker<V>
Cette interface est l'entit parente des classes concrtes qui permettent de lancer une tche de longue dure en
fond de lUI. Vous ne serez pas amen la manipuler directement, cependant vous pouvez remarquer la prsence
de plusieurs mthodes retournant des proprits telles que progress, message, title, etc. qui permettent de suivre
de la tche depuis l'UI.
Le type V pass en paramtre de Worker est tout simplement le type de la valeur qui sera retourne par la mthode
call(). C'est--dire le type du rsultat de la tche, car cette mthode est celle qui sera appele pour effectuer la
tche de longue dure. Gnralement, pour des tches ne retournant pas de valeur, V sera soit non dclar (et donc
implicitement de type Object) ou de type Void avec une valeur de retour null dans les deux cas.
Note : java.lang.Void est un type spcial de la JVM dont la seule valeur possible est null.
Un Worker dispose galement d'une proprit tat, state, qui changera de valeur au cours de la vie de la tche. Les
valeurs possibles de cet tat sont dfinies dans l'enum Worker.State :

READY - la tche est prte tre excute ; c'est l'tat initial ;


SCHEDULED - la tche est prvue pour tre excute ; il s'agit d'un tat intermdiaire entre READY et
RUNNING ;
RUNNING - la tche dmarre son excution ;
FAILED - la tche a chou, gnralement cause d'une exception qui a t leve au cours du traitement ;
CANCELLED - la tche a t annule, gnralement par l'utilisateur depuis l'UI ;
SUCCEEDED - la tche s'est correctement excute et a retourn son rsultat.

Cette interface est implmente par trois classes abstraites :

Service<V> : une classe qui est instancie et manipule par l'UI pour crer, grer et superviser la tche de
longue dure ;
ScheduledService<V> : une classe qui est instancie et manipule par l'UI pour crer, grer et superviser la
tche de longue dure qui peut se rpter intervalles rguliers. JDK8 ou ultrieur ;
Task<V> : une classe qui est instancie et manipule par le service ou le service rptable pour excuter la
tche de longue dure dans un thread spar.

Figure 2 - Hirarchie des classes.

III-B - javafx.concurrent.Service<V>
La classe Service est destine tre utilise et manipule dans l'UI. Son seul vrai but est de crer une tche (Task)
qui sera excute en arrire-plan dans un autre thread. Une instance de Service est destine tre rutilisable,
vous pouvez donc conserver une rfrence sur une instance de Service et l'excuter plusieurs fois d'affile.
Pour instancier un service, il suffit de crer une nouvelle classe anonyme qui tend Service<V> et de surcharger la
mthode abstraite createTask() pour qu'elle cre une Task<V> du mme type que le service :
1. Service<Image> imageLoadingService = new Service<Image>(){
2.
3.
@Override
4.
protected Task<Image> createTask() {
5.
// Instancier et retourner une Task<Image> ici.
- 10 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

6.
}
7. };

On peut aussi tendre Service<V> dans une classe part :


1. public class ImageLoadingService extends Service<Image> {
2.
3.
@Override
4.
protected Task<Image> createTask() {
5.
// Instancier et retourner une Task<Image> ici.
6.
}
7. };

III-C - javafx.concurrent.Task<V>
C'est cette classe qui sera amene faire le calcul, la requte ou le traitement de longue dure et c'est dans la
mthode call() de cette classe que la tche est effectue ; cette mthode sera appele dans un thread spar,
diffrent du JavaFX Application Thread.
Contrairement Service, une Task est destine n'tre excute qu'une seule et unique fois. Une tche n'est pas
rutilisable !
Pour crer une tche, il suffit de crer une nouvelle classe anonyme qui tend Task<V> et de surcharger la mthode
call() pour qu'elle renvoie une instance du type appropri :
1. Task<Image> imageLoadingTask = new Task<Image>(){
2.
3.
@Override
4.
protected Image call() throws Exception {
5.
// Charger l'image ici.
6.
Image image = ...
7.
return image;
8.
}
9. };

On peut aussi tendre Task<V> dans une classe part :


1. public class ImageLoadingTask extends Task<Image> {
2.
3.
@Override
4.
protected Image call() throws Exception {
5.
// Charger l'image ici.
6.
Image image = ...
7.
return image;
8.
}
9. }

En plus des calculs, accs IO, requte web ou BD ou autre, vous pouvez faire tout ce que vous faites habituellement
dans un thread au cours de ce traitement : boucle infinie, appel Thread.sleep(), etc. sans pour autant que cela
ne bloque l'UI.
Attention cependant, toute exception qui n'est pas catche dans ce traitement remontera au niveau suprieur et
passera automatiquement la tche en l'tat FAILED en plus d'arrter le calcul ou le traitement en cours.

III-D - Javafx.concurrent.ScheduledService<V>
La classe ScheduledService, introduite dans le JDK8 permet de crer des services qui se rptent intervalles
rguliers. Elle fonctionne comme la class Service mais dispose de proprits supplmentaires pour grer la rptition
ainsi que les conditions de gestion des checs.
- 11 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Un ScheduledService, se relance automatiquement lorsque son excution prcdente s'est termine correctement
une fois le temps d'attente spcifi par le programmeur coul. Il peut galement se relancer en cas d'erreur
d'excution suivant les conditions de gestion des checs qui ont t spcifies.

IV - Utiliser un service
Vous serez donc amen utiliser la classe Service depuis le code de votre UI, en raction un clic sur un bouton, dans
un menu, une slection dans une table, une liste ou un menu droulant, etc. La majorit de ces actions s'excutera
donc dans le JavaFX Application Thread. Voyons donc maintenant comment utiliser un service.

IV-A - Cration et lancement


Reprenons notre code qui permet d'initialiser notre service et compltons-le avec le code qui initialise la tche :
1. Service<Image> imageLoadingService = new Service<Image>(){
2.
3.
@Override
4.
protected Task<Image> createTask() {
5.
return new Task<Image>(){
6.
7.
@Override
8.
protected Image call() throws Exception {
9.
// Charger l'image ici.
10.
Image image = ...
11.
return image;
12.
}
13.
};
14.
}
15. };
16. imageLoadingService.start();

Ou, pour une tche qui ne retourne rien :


1. Service<Void> fileSaveService = new Service<Void>(){
2.
3.
@Override
4.
protected Task<Void> createTask() {
5.
return new Task<Void>(){
6.
7.
@Override
8.
protected Void call() throws Exception {
9.
// Sauvegarder le fichier ici.
10.
[...]
11.
return null;
12.
}
13.
};
14.
}
15. };
16. fileSaveService.start();

L'appel la mthode start() du service, provoquera le dmarrage de la tche de fond dans un nouveau thread.

IV-B - Annulation
Pour annuler un service en cours d'excution, il suffit d'appeler la mthode cancel() du service depuis l'UI (par
exemple, un bouton). Lorsque la mthode cancel() est appele, l'tat du service et de la tche passe CANCELLED.
Attention, cela n'aura pas pour effet d'arrter la tche immdiatement ! Comme nous le verrons ultrieurement,
vous devrez prendre des prcautions supplmentaires dans la tche pour vous assurer qu'elle termine bien son
excution.
- 12 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

1. imageLoadingService.cancel();

IV-C - Relancer un service


Pour qu'un service puisse tre relanc, il doit tre dans l'tat READY, faute de quoi tout appel la mthode start() se
soldera par la leve d'une exception de type IllegalStateException. Il convient donc d'appeler sa mthode reset() avant
d'appeler la mthode start(). l'excution, une nouvelle instance de Task sera alors cre et excute par le service.
1. imageLoadingService.reset();
2. imageLoadingService.start();

Il est galement possible d'appeler la mthode restart() qui fera exactement la mme chose que le couple reset()
+ start() :
1. imageLoadingService.restart();

IV-D - Avancement du service


Il est possible de binder les proprits retournes par progressProperty(), messageProperty(), titleProperty(), etc.
d'un service vers des lments de l'UI tels que des instances de Label, ProgressBar ou ProgressIndicator de manire
suivre l'avancement de l'excution et les changements d'tat de la tche de fond.
1. Stage dialog = ...
2. ProgressIndicator progressIndicator = ...
3. Label informationLabel = ...
4. [...]
5. Service<Image> imageLoadingService = new Service<Image>() {
6.
[...]
7. };
8. dialog.titleProperty().bind(imageLoadingTask.titleProperty());
9. progressIndicator.progressProperty.bind(imageLoadingTask.progressProperty()) ;
10. informationLabel.textProperty().bind(imageLoadingTask.messageProperty());
11. imageLoadingService.start();

Il est aussi possible de mettre des ChangeListener sur chacune de ces proprits pour transmettre leurs changements
de valeur sur d'autres lments de l'UI.

IV-E - tat du service


En gnral vous aurez envie de superviser les changements d'tat d'un service de manire afficher des messages
d'erreur ou de succs d'opration dans votre UI.

Figure 3 - Les divers tats d'un service.


Pour superviser les changements d'tat d'une tche de fond, vous avez deux possibilits.

- 13 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

IV-E-1 - ChangeListener sur l'tat du service


Vous pouvez mettre un couteur de type ChangeListener sur la proprit state du service et superviser les
changements de valeur de cette proprit :
Code JDK7

1. Service<Image> imageLoadingService = new Service<Image>() {


2.
[...]
3. };
4. imageLoadingService.stateProperty().addListener(new ChangeListener<Worker.State>() {
5.
@Override
6.
public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue,
Worker.State newValue) {
7.
switch (newValue) {
8.
case READY:
9.
// La tche est prte.
10.
[...]
11.
break;
12.
case SCHEDULED:
13.
// La tche va tre excute.
14.
[...]
15.
break;
16.
case RUNNING:
17.
// La tche est en cours d'excution.
18.
[...]
19.
break;
20.
case FAILED:
21.
// La tche a chou.
22.
[...]
23.
break;
24.
case CANCELLED:
25.
// La tche a t annule.
26.
[...]
27.
break;
28.
case SUCCEEDED:
29.
// La tche s'est correctement termine.
30.
[...]
31.
break;
32.
}
33.
}
34. });
35. imageLoadingService.start();

Code JDK8

1. Service<Image> imageLoadingService = new Service<Image>() {


2.
[...]
3. };
4. imageLoadingService.stateProperty().addListener((ObservableValue<? extends Worker.State> observable,
Worker.State oldValue, Worker.State newValue) -> {
5.
switch (newValue) {
6.
case READY:
7.
// La tche est prte.
8.
[...]
9.
break;
10.
case SCHEDULED:
11.
// La tche va tre excute.
12.
[...]
13.
break;
14.
case RUNNING:
15.
// La tche est en cours d'excution.
16.
[...]
17.
break;
18.
case FAILED:
19.
// La tche a chou.
20.
[...]
21.
break;
22.
case CANCELLED:
23.
// La tche a t annule.
- 14 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK8

24.
[...]
25.
break;
26.
case SUCCEEDED:
27.
// La tche s'est correctement termine.
28.
[...]
29.
break;
30.
}
31. });
32. imageLoadingService.start();

IV-E-2 - Callback sur le service


Mais l'API vous permet galement de mettre des callbacks en place, par exemple :
Code JDK7

1. Service<Image> imageLoadingService = new Service<Image>() {


2.
[...]
3. };
4. imageLoadingService.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
5.
6.
@Override
7.
public void handle(WorkerStateEvent event) {
8.
// La tche s'est correctement termine.
9.
[...]
10.
}
11. });
12. imageLoadingService.setOnFailed(new EventHandler<WorkerStateEvent>(){
13.
14.
@Override
15.
public void handle(WorkerStateEvent event) {
16.
// La tche a chou.
17.
[...]
18.
}
19. });
20. imageLoadingService.start();

Code JDK8

1. Service<Image> imageLoadingService = new Service<Image>() {


2.
[...]
3. };
4. imageLoadingService.setOnSucceeded((WorkerStateEvent event) -> {
5.
// La tche s'est correctement termine.
6.
[...]
7. });
8. imageLoadingService.setOnFailed((WorkerStateEvent event) -> {
9.
// La tche a chou.
10.
[...]
11. });
12. imageLoadingService.start();

Il existe des callbacks pour tous les tats possibles :

setOnReady() - la tche est prte ;


setOnScheduled() - la tche va tre excute ;
setOnRunning() - la tche dmarre son excution ;
setOnFailed() - la tche a chou ;
setOnCancelled() - la tche a t annule ;
setOnSucceded() - la tche s'est correctement termine.

Toutes ces mthodes sont appeles depuis le JavaFX Application Thread.

- 15 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

IV-F - Rcuprer le rsultat


Pour rcuprer le rsultat d'un service, il suffit d'appeler la mthode getValue() du service dans le listener ou le
callback appropri. Ainsi le rsultat de la tche pourra tre transmis l'UI ou un autre traitement.
Code JDK7

1. // Le service est final, car il doit tre appel depuis le callback.


2. final Service<Image> imageLoadingService = new Service<Image>() {
3.
[...]
4. };
5. imageLoadingService.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
6.
7.
@Override
8.
public void handle(WorkerStateEvent event) {
9.
// La tche s'est correctement termine.
10.
final Image image = imageLoadingService.getValue();
11.
[...]
12.
}
13. });
14. imageLoadingService.start();

Code JDK8

1. // Le service est final, car il doit tre appel depuis le callback.


2. final Service<Image> imageLoadingService = new Service<Image>() {
3.
[...]
4. };
5. imageLoadingService.setOnSucceeded((WorkerStateEvent event) -> {
6.
// La tche s'est correctement termine.
7.
final Image image = imageLoadingService.getValue();
8.
[...]
9. });
10. imageLoadingService.start();

IV-G - Rcuprer la raison de l'chec


Pour rcuprer la raison de l'chec d'un service, il suffit d'appeler la mthode getException() du service dans le
listener ou le callback appropri. Cela permettra d'afficher l'erreur l'utilisateur ou de la stocker dans un journal.
Code JDK7

1. // Le service est final, car il doit tre appel depuis le callback.


2. final Service<Image> imageLoadingService = new Service<Image>() {
3.
[...]
4. };
5. imageLoadingService.setOnFailed(new EventHandler<WorkerStateEvent>(){
6.
7.
@Override
8.
public void handle(WorkerStateEvent event) {
9.
// La tche a chou.
10.
final Throwable error = imageLoadingService.getException();
11.
[...]
12.
}
13. });
14. imageLoadingService.start();

Code JDK8
1.
2.
3.
4.
5.
6.
7.
8.
9.

// Le service est final, car il doit tre appel depuis le callback.


final Service<Image> imageLoadingService = new Service<Image>() {
[...]
};
imageLoadingService.setOnFailed((WorkerStateEvent event) -> {
// La tche a chou.
final Throwable error = imageLoadingService.getException();
[...]
});

- 16 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK8

10. imageLoadingService.start();

V - Utiliser une tche


De manire gnrale, la classe Task sera uniquement manipule par votre service et la majorit de son code
s'excutera dans son propre Thread, indpendant du JavaFX Application Thread. Voyons donc maintenant comment
utiliser une tche.

V-A - Notification de l'avancement


Depuis la mthode call() de la tche, il est possible d'envoyer des informations vers l'UI via les mthodes
updateProgress(), updateMessage(), etc. Les valeurs passes ces mthodes seront transmises vers le JavaFX
Application Thread.
1. return new Task<Integer>() {
2.
@Override
3.
protected Integer call() throws Exception {
4.
final int maxIterations = 1000000;
5.
int iterations = 0;
6.
for (iterations = 0; iterations < maxIterations; iterations++) {
7.
// On envoie un nouveau message.
8.
updateMessage("iteration "+ (iterations+1));
9.
// On change l'avancement de la tche.
10.
updateProgress(iterations, maxIterations);
11.
[...]
12.
}
13.
return iterations;
14.
}
15.
updateMessage("Done");
16.
updateProgress(iterations, maxIterations);
17. };

Si trop d'appels ces mthodes ont lieu dans un court laps de temps, il y aura agrgation des diffrents appels et
seules les dernires valeurs seront transmises l'UI. Ainsi certains messages peuvent tre perdus et la progression
peut sauter des valeurs si elle est trop rapide.
Dans le cas de boucles ou de progressions obtenues par calcul, il faut galement faire attention au fait que
updateProgress() gnrera une exception de type IllegalArgumentException si la valeur d'avancement devient
strictement suprieure la valeur maximum attendue.

V-B - Annulation
Comme nous l'avons vu prcdemment, appeler la mthode cancel() du service met la tche dans l'tat CANCELLED,
mais cela ne suffit pas en soi pour arrter l'excution de la tche. Dans votre traitement, vous devez vrifier
intervalles rguliers la valeur retourne par la mthode isCancelled() de la tche et interrompre le calcul en cours
le cas chant . Par exemple, la tche suivante effectue 1 000 000 itrations et vrifie si la tche a t annule
chaque itration, interrompant la boucle si c'est le cas.
1. return new Task<Integer>() {
2.
@Override
3.
protected Integer call() throws Exception {
4.
final int maxIterations = 1000000;
5.
int iterations = 0;
6.
for (iterations = 0; iterations < maxIterations; iterations++) {
7.
// Arrt de la boucle si la tche est annule.
8.
if (isCancelled()) {
9.
break;
10.
}
11.
// On envoie un nouveau message.
- 17 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

12.
updateMessage("iteration "+ (iterations+1));
13.
// On change l'avancement de la tche.
14.
updateProgress(iterations, maxIterations);
15.
[...]
16.
}
17.
return iterations;
18.
}
19. };

V-B-1 - Lors d'un appel bloquant


Lorsque l'on effectue un appel bloquant, par exemple en mettant le thread courant en pause dans une boucle, il est
important de vrifier l'tat de la tche une fois que le thread courant est nouveau actif. En effet, le thread peut se
rveiller du fait que la tche est passe dans l'tat CANCELLED.
1. return new Task<Integer>() {
2.
@Override
3.
protected Integer call() throws Exception {
4.
final int maxIterations = 1000000;
5.
int iterations = 0;
6.
for (iterations = 0; iterations < maxIterations; iterations++) {
7.
// Arrt de la boucle si la tche est annule.
8.
if (isCancelled()) {
9.
break;
10.
}
11.
// On envoie un nouveau message.
12.
updateMessage("iteration "+ (iterations+1));
13.
// On change l'avancement de la tche.
14.
updateProgress(iterations, maxIterations);
15.
[...]
16.
try {
17.
Thread.sleep(100);
18.
} catch (InterruptedException ie) {
19.
if (isCancelled()) {
20.
break;
21.
}
22.
}
23.
}
24.
return iterations;
25.
}
26. };

V-C - tat d'une tche


Il est galement possible de superviser les changements d'tat d'une tche. cet effet, il est possible de surcharger
les mthodes scheduled(), running(), failed(), cancelled(), et succeeded() de la classe Task.

scheduled() - la tche va tre excute.


running() - la tche dmarre son excution.
failed() - la tche a chou.
cancelled() - la tche a t annule.
succeeded() - la tche s'est correctement termine.

Il n'existe pas de mthode pour l'tat READY, car il s'agit de l'tat par dfaut d'une tche sa cration.

- 18 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Figure 4 - Les divers tats d'une tche.


Toutes ces mthodes sont appeles depuis le JavaFX Application Thread une fois que les propagations aux listeners
ont t termines.

V-D - Produire des rsultats partiels


V-D-1 - JDK 7
Dans le JDK7, Task ne produit qu'un seul et unique rsultat : son rsultat final. Cependant, il est assez facile d'ajouter
la capacit de produire des rsultats partiels en utilisant des proprits JavaFX sur la tche.
1. public class IterationsTask extends Task<Integer> {
2.
3.
private final IntegerProperty
partialResult = new SimpleIntegerProperty(this, "partialResult", -1);
4.
5.
public final int getPartialResults() {
6.
return partialResult.get();
7.
}
8.
9.
public final ReadOnlyIntegerProperty partialResultProperty() {
10.
return partialResult;
11.
}
12.
13.
[...]
14. }

Il faudra cependant, dans la mthode call(), prendre des prcautions pour que les valeurs soient settes dans le
JavaFX Application Thread et non pas dans le thread de la tche. En effet, gnralement des rsultats partiels ne
sont intressants que dans le cadre d'un affichage dans une UI. Or, une fois affiches dans une scne, les proprits
des contrles JavaFX doivent tre uniquement accdes via le JavaFX Application Thread.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

@Override
protected Integer call() throws Exception {
final int maxIterations = 1000000;
int iterations = 0;
for (iterations = 0; iterations < maxIterations; iterations++) {
// Arrt de la boucle si la tche est annule.
if (isCancelled()) {
break;
}
// ! La proprit doit tre manipule dans la JavaFX Application Thread !
final int it = iterations;
Platform.runLater(new Runnable() {
@Override
public void run() {
partialResult.set(it);
}
});
}
- 19 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

19.
20.

return iterations;

Il faut faire galement attention au fait qu'ici, il n'y a pas de fusion ou agrgation des envois de valeurs partielles
sur le JavaFX Application Tread. Cela peut donc provoquer une monopolisation ou une surcharge du thread par la
tche cause du trop grand nombre de requtes Platform.runLater() et nous ramener notre problme de blocage
initial. Ici, cet exemple ne fait pas moins de 1 000 000 de requtes Platform.runLater() dans une boucle rapide
sans aucune pause ! Cela peut faire que l'application cesse de rpondre, ne puisse plus se redimensionner ou plante
carrment, bref on perdrait tous les avantages lis l'utilisation d'un service !

Il faudra donc penser amnager des temps de repos en appelant Thread.sleep() intervalles rguliers de manire
ce que le JavaFX Application Thread puisse continuer s'excuter et vacuer les vnements en attente.
1.
@Override
2.
protected Integer call() throws Exception {
3.
final int maxIterations = 1000000;
4.
int iterations = 0;
5.
for (iterations = 0; iterations < maxIterations; iterations++) {
6.
// Arrt de la boucle si la tche est annule.
7.
if (isCancelled()) {
8.
break;
9.
}
10.
// ! La proprit doit tre manipule dans la JavaFX Application Thread !
11.
final int it = iterations;
12.
Platform.runLater(new Runnable() {
13.
@Override
14.
public void run() {
15.
partialResult.set(it);
16.
}
17.
});
18.
// Interragir ainsi avec le JavaFX Application Thread demande de lui laisser un peu le temps de respirer.
19.
try {
20.
Thread.sleep(50);
21.
} catch (InterruptedException ie) {
22.
if (isCancelled()) {
23.
break;
24.
}
25.
}
26.
}
27.
return iterations;
28.
}

On pourrait galement modifier le code pour faire en sorte que les mises jour de l'UI arrivent tous les 1000 lments
par exemple, de manire rduire le nombre de mises jour effectues.

V-D-2 - JDK 8
Le JDK 8 a ajout la possibilit d'invoquer la mthode updateValue() dans le corps de la tche pour mettre jour
le contenu de la proprit value de l'objet Task (et de son service parent). Il suffira alors de placer un couteur de
type InvalidationListener ou ChangeListener sur cette proprit pour tre mis au courant de ses changements de
valeur au cours du calcul.
La mthode updateValue() peut tre invoque depuis n'importe quel thread mme celui dans lequel s'excute la
tche. Tout comme les autres mthodes updateXXX(), les appels sont fusionns lorsqu'ils sont trop nombreux, ce
qui fait que certains changements de valeur peuvent tre omis.
Code JDK8

@Override
protected Integer call() throws Exception {
final int maxIterations = 1000000;
- 20 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

Code JDK8

int iterations = 0;
for (iterations = 0; iterations < maxIterations; iterations++) {
// Arrt de la boucle si la tche est annule.
if (isCancelled()) {
break;
}
// ! La proprit doit tre manipule dans la JavaFX Application Thread !
final int it = iterations;
updateValue(it);
}
}
return iterations;

V-E - Utiliser une tche dans un contexte non JavaFX


Vous pouvez tre amen utiliser une tche dans un contexte non JavaFX, par exemple, dans le but d'effectuer des
tests unitaires. De plus, la classe Task<V> hrite des interfaces Runnable et RunnableFuture<V>, il est donc tout
fait possible de la passer un Thread Java normal pour excution :
1. final Thread imageLoadingThread = new Thread(imageLoadingTask);
2. imageLoadingThread.start();

Il est galement possible de passer une tche un ExecutorService :


1. anExecutorService.submit(imageLoadingTask);

Cependant, vous serez rarement amen utiliser ces cas de figure.


Attention, si vous utilisez Task dans un contexte o le JavaFX Application Thread n'a pas t initialis, il ne faut
pas appeler updateMessage() ou updateProgress() ou n'importe quelle autre mthode updateXXX() sous peine de
gnrer des exceptions. De plus, cette approche peut ne pas fonctionner non plus avec des tches produisant
des rsultats partiels telles que celles que nous avons montres pour le JDK 7 puisqu'elles invoquent la mthode
Platform.runLater() qui suppose que les runtimes JavaFX ont t correctement initialiss.

VI - Utiliser un service rptable


La classe ScheduledService, introduite dans le JDK8, permet de crer un service qui s'excute de manire cyclique.
Cette classe s'utilise exactement comme la classe Service, mais introduit de nouveaux concepts et proprits.
Lorsque le service s'excute correctement, il redmarre automatiquement aprs un temps d'attente. Au contraire,
lorsque le service choue, le programmeur peut dcider s'il se relance en fonction de plusieurs critres. tant donn
que le service se rpte automatiquement de manire cyclique, il repasse rgulirement par tous les tats possibles
au cours de son excution.
Attention, la gestion des dures par cette classe est indicative ; il ne faut pas utiliser ce genre de service pour des
tches demandant une prcision extrme dans la gestion du temps.

VI-A - Dlai avant l'excution


Tout d'abord, la proprit delay permet de spcifier un temps d'attente avant le dmarrage initial du service.
1. service.setDelay(Duration.seconds(5));

- 21 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

VI-B - Priode d'excution


La proprit period permet de dfinir le temps d'attente entre deux excutions sans erreur. C'est--dire le temps qui
s'coule entre chaque moment dans lequel l'tat du service entre en l'tat RUNNING.
1. service.setPeriod(Duration.seconds(1));

Si la tche prend plus de temps s'excuter que la dure spcifie dans la priode, ou si la priode est vide (ex. :
Duration.ZERO ou une dure indtermine), le service se relance alors immdiatement aprs la fin de la tche sans
plus attendre. De plus, le service ne peut interrompre une tche en cours si elle dpasse le temps d'attente indiqu
pour la priode.

VI-C - Gestion des checs


Par dfaut, en cas d'chec, le service se termine immdiatement. Il est possible de changer ce comportement en
modifiant la valeur de la proprit restartOnFailure pour indiquer si le service doit se relancer en cas d'chec. Il est
galement possible de modifier la valeur de la proprit maxFailureCount pour indiquer le nombre maximal d'checs
supports. La proprit en lecture seule currentFailureCount permet de connaitre le nombre d'checs encourus par
le service jusqu' prsent.
1. service.setRestartOnFailure(true); // On continue l'excution en cas d'erreur.
2. service.setMaxFailureCount(100); // Et au maximum on acceptera 100 checs.

La proprit backoffStrategy permet de spcifier une fabrique dures de type Callback<ScheduledService<?>,


Duration> qui sera invoque lorsque le service a chou. Cela permet par exemple, d'augmenter, le temps d'attente
entre chaque chec successif. Cette valeur sera ajoute la priode du service lors du temps d'attente avant la
prochaine tentative d'excution. Cette valeur peut tre plafonne en utilisant la proprit maximumCumulativePeriod.

1. service.setBackoffStrategy(service ->
Duration.seconds(service.getCurrentFailureCount() * 5)); // On ajoute 5 secondes d'attente entre chaque chec.
2. service.setMaximumCumulativePeriod(Duration.minutes(10)); // Au maximum on attendra 10 minutes avant la prochai

VI-D - Derniers bons rsultats


La valeur de la proprit value du service sera rinitialise null avant chaque fois que le service se relance.
Il est donc plus intressant de se pencher sur la proprit en lecture seule lastValue, qui contient la dernire valeur
gnre lors d'une excution correcte de la tche. Elle est initialement la valeur null et ne sera mise jour que
lorsque la tche s'excute correctement. Il est donc possible de suivre ses changements de valeur en lui ajoutant un
couteur de type InvalidationListener ou ChangeListener.

VII - Conclusion
Vous savez dsormais comment lancer une tche de fond en JavaFX, ceci vous permettra de lancer tous vos
traitements longs en arrire-plan de l'UI sans pour autant geler votre interface graphique.

VIII - Remerciements
Je tiens remercier toute l'quipe du forum
Dveloppez ainsi que
Mickael Baron et
Gueritarish pour
leurs suggestions et leurs relectures du prsent article. Je tiens galement remercier Claude Leloup pour ses
corrections orthographiques.

- 22 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

Tutoriel sur l'excution d'une tche de fond en JavaFX par Fabrice Bouy

IX - Liens

Page javadoc de l'interface Worker<V>.


Page javadoc de la classe Service<V>.
Page javadoc de la classe ScheduledService<V>.
Page javadoc de la classe Task<V>.

- 23 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2013 Fabrice Bouy. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://fabrice-bouye.developpez.com/tutoriels/javafx/gui-service-tache-de-fond-thread-javafx/

You might also like