FAQ JavaFXConsultez toutes les FAQ

Nombre d'auteurs : 4, nombre de questions : 507, dernière mise à jour : 2 novembre 2016  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur le forum JavaFX de http://java.developpez.com ainsi que l'expérience personnelle des auteurs.

Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.


SommaireConcurrence (19)
précédent sommaire suivant
 

JavaFX utilise trois threads principaux :

  • le JavaFX Application Thread - il s'agit du thread événementiel de l'interface graphique, du SceneGraph et de la scène. Les nœuds graphiques qui sont attachés à une scène ne peuvent être manipulés que dans ce thread. Ce thread est celui dans lequel vous travaillerez et dans lequel votre code s’exécutera la plupart du temps ;
  • le thread de rendu Prism - il s'agit du thread qui est utilisé pour effectuer le moteur de rendu Prism qui se charge du rendu à l’écran. Ce thread n'est normalement pas directement accessible au programmeur ;
  • le thread média - ce thread qui tourne en tâche de fond et est utilisé pour synchroniser l’affichage en mettant à jour, par exemple, les intervalles (étapes intermédiaires) lors d'animations et de transitions. Ce thread n'est normalement pas directement accessible au programmeur.

Mis à jour le 1er octobre 2014 bouye

Il peut arriver que vous ayez besoin d'effectuer une tâche de longue durée (traitement long, lecture/écriture de fichier, vous connecter à une base de données, à un web service, etc.). JavaFX met à disposition, dans le package javafx.concurrent, une API de concurrence qui permet de gérer de telles tâches sans pour autant bloquer le JavaFX Application Thread.

Pour utiliser cette API de concurrence, vous allez devoir utiliser des objets d'un des trois types suivants :

  • Service<V> - qui est une classe service à manipuler depuis votre UI, dans le JavaFX Application Thread. Une instance de la classe Service peut être réutilisée plusieurs fois.
  • ScheduledService<V> - qui est une classe service à manipuler depuis votre UI, dans le JavaFX Application Thread. Ce genre de service s’exécute de manière cyclique à intervalles réguliers - JDK8 ou ultérieur.
  • Task<V> - qui est la classe qui se chargera d’exécuter la tâche de longue durée. Elle s’exécutera dans son propre thread, sans interférer ni bloquer le JavaFX Application Thread. Une instance de la classe Task ne peut être utilisée qu'une seule et unique fois. La tâche est créée par le service.


Ces trois classes implémentent l'interface javafx.concurrent.Worker<V>.

Ici, le type V est le type de retour de la tâche et du service. Un service ou une tâche qui ne produit rien sera de type java.lang.Void avec pour valeur de retour la valeur null (la seule valeur acceptée pour ce type).

Mis à jour le 15 mars 2015 bouye

Pour créer une tâche de fond, depuis notre IU (par exemple en réponse à un clic sur un bouton), nous allons créer une instance de la classe abstraite Service<V> avec le type de retour approprié. Nous allons ensuite surcharger la méthode createTask() de manière à initialiser une instance de la classe abstraite Task<V>. Dans cette tâche, nous allons surcharger la méthode call() pour effectuer le traitement de longue durée. Le résultat du traitement sera retourné en fin de méthode.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final Service<Integer> service = new Service<Integer>() { 
  
    @Override 
    protected Task<Integer> createTask() { 
        return new Task<Integer>() { 
  
            @Override 
            protected Integer call() throws Exception { 
                int result = 0; 
                // Faire le traitement ici. 
                return result; 
            }                             
        }; 
    }                     
};

Ici, nous avons créé un service et une tâche qui produisent une valeur entière.

Dans le cas où nous ne produisons rien, il faut procéder comme suit :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
final Service<Void> service = new Service<Void>() { 
  
    @Override 
    protected Task<Void> createTask() { 
        return new Task<Void>() { 
  
            @Override 
            protected Void call() throws Exception { 
                // Faire le traitement ici. 
                return null; 
            }                             
        }; 
    }                     
};

Ici, null est la seule valeur utilisable avec le type java.lang.Void.

Mis à jour le 2 octobre 2014 bouye

Il est possible de surveiller les changements d’état d'un service en plaçant un écouteur de type InvalidationListener ou ChangeListener sur sa propriété state.



Cette propriété, de type Worker.State, peut avoir les états suivants :

  • READY - le service est prêt à être lancé : il vient d’être créé ou il a été réinitialisé ;
  • SCHEDULED - le service a été lancé, mais n'a pas encore démarré ;
  • RUNNING - le service a démarré, le traitement est en cours ;
  • CANCELLED - le service a été annulé ;
  • FAILED - le service a échoué (ex. : une exception a été lancée au cours du traitement) ;
  • SUCCEEDED - le service s'est terminé sans erreur. Il est possible d'accéder au résultat du traitement ou de relancer le service.


Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
service.stateProperty().addListener(observable -> { 
    switch (service.getState()) { 
        // Traiter les différents cas ici. 
    } 
});

Les modifications de valeur seront reçues sur le JavaFX Application Thread.

La classe Service<V> dispose aussi de callbacks qu'il est possible d'invoquer quand le service change d’état :

  • setOnReady() - le service est passé dans l’état READY ;
  • setOnScheduled() - le service est passé dans l’état SCHEDULED ;
  • setOnRunning() - le service est passé dans l’état RUNNING ;
  • setOnCancelled() - le service est passé dans l’état CANCELLED ;
  • setOnFailed() - le service est passé dans l’état FAILED ;
  • setOnSucceeded() - le service est passé dans l’état SUCCEEDED.


Ces callback prennent un paramètre de type EventHandler<WorkerStateEvent>.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() { 
  
    @Override 
    public void handle(WorkerStateEvent workerStateEvent) { 
        System.out.println("Traitement terminé !"); 
    } 
});

Ou :

Code Java : Sélectionner tout
service.setOnSucceeded(workerStateEvent -> System.out.println("Traitement terminé !"));

Mis à jour le 2 octobre 2014 bouye

Il est possible de surveiller les changements d’état d'une tâche en plaçant un écouteur de type InvalidationListener ou ChangeListener sur sa propriété state.



Cette propriété, de type Worker.State, peut avoir les états suivants :

  • READY - la tâche est prête à être lancée : elle vient d’être créée ;
  • SCHEDULED - la tâche a été lancée, mais n'a pas encore démarré ;
  • RUNNING - la tâche a démarré, le traitement est en cours ;
  • CANCELLED - la tâche a été annulée ;
  • FAILED - la tâche a échoué (ex. : une exception a été lancée au cours du traitement) ;
  • SUCCEEDED - la tâche s'est terminée sans erreur. Il est possible d'accéder au résultat du traitement.


Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
task.stateProperty().addListener(observable -> { 
    switch (task.getState()) { 
        // Traiter les différents cas ici. 
    } 
});

Les modifications de valeur seront reçues sur le JavaFX Application Thread.

La classe Task<V> dispose aussi de callbacks qu'il est possible d'invoquer quand la tâche change d’état :

  • setOnScheduled() - le service est passé dans l’état SCHEDULED ;
  • setOnRunning() - le service est passé dans l’état RUNNING ;
  • setOnCancelled() - le service est passé dans l’état CANCELLED ;
  • setOnFailed() - le service est passé dans l’état FAILED ;
  • setOnSucceeded() - le service est passé dans l’état SUCCEEDED.


Ici, il n'existe pas de callback setOnReady(), car la tâche est dans l’état READY dès sa création. De plus, une tâche ne peut pas être réutilisée et donc elle ne peut plus jamais revenir à cet état après son lancement.

Ces callback prennent un paramètre de type EventHandler<WorkerStateEvent>.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() { 
  
    @Override 
    public void handle(WorkerStateEvent workerStateEvent) { 
        System.out.println("Traitement terminé !"); 
    } 
});

Ou :

Code Java : Sélectionner tout
task.setOnSucceeded(workerStateEvent -> System.out.println("Traitement terminé !"));

Mis à jour le 2 octobre 2014 bouye

Pour démarrer un service, il suffit d'invoquer la méthode start() de ce service.

Par exemple :

Code Java : Sélectionner tout
service.start();

Cela aura pour effet de créer une nouvelle instance de la tâche, puis de la placer à l’état SCHEDULED. La tâche sera alors exécutée dans un autre thread ce qui placera la tâche et le service dans l’état RUNNING. Lorsque la tâche se terminera, elle atteindra l’état CANCELLED, FAILED ou SUCCEEDED qui sera transmis au service. Cette méthode ne doit être invoquée que depuis le JavaFX Application Thread.

Une exception sera levée si le service n'est pas dans l’état READY lorsque la méthode start() est invoquée.

Mis à jour le 2 octobre 2014 bouye

Pour annuler un service, il suffit d'invoquer la méthode cancel() de ce service.

Par exemple :

Code Java : Sélectionner tout
service.cancel();

Cette méthode retourne la valeur true lorsque le service a pu changer d’état. Elle retournera false si le service est déjà dans l’état CANCELLED ou s'il est dans l’état SUCCEEDED.

Invoquer cette méthode ne stoppe pas immédiatement la tâche, elle positionne juste un sémaphore qui indique à la tâche qu'elle doit s’arrêter.

Mis à jour le 2 octobre 2014 bouye

Pour savoir si une tâche a été annulée, le programmeur doit régulièrement tester la valeur de retour de la méthode isCancelled() de la tâche durant son traitement. Si cette méthode retourne true, c'est que la tâche a été annulée et que le traitement doit être interrompu.

Par exemple, le code suivant est une tâche qui teste si un nombre donné est premier :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class PrimeTestTask extends Task<Boolean> { 
  
    private final int value; 
  
    public PrimeTestTask(final int value) { 
        this.value = Math.abs(value); 
    } 
  
    @Override 
    protected Boolean call() throws Exception { 
        Boolean result = Boolean.TRUE; 
        if (value == 0 || value == 1 || (value > 2 && value % 2 == 0)) { 
            result = Boolean.FALSE; 
        } else if (value > 2) { 
            for (int test = 3; test * test < value; test += 2) { 
                // Le traitement a été annulé. 
                if (isCancelled()) { 
                    return null; 
                } 
                if (value % test == 0) { 
                    result = Boolean.FALSE; 
                    break; 
                } 
            } 
        } 
        return result; 
    } 
}

Ici, lorsque la méthode isCancelled() retourne la valeur true, nous retournons une valeur indéterminée et nous sortons de la tâche.

Mis à jour le 2 octobre 2014 bouye

Pour pouvoir redémarrer un service, il faut que ce dernier soit à nouveau dans un état READY. Il est possible de réinitialiser la valeur de cet état en appelant la méthode reset() du service.



Par exemple :

Code Java : Sélectionner tout
service.reset();

Si cette méthode est invoquée alors que le service n'est pas dans l’état READY, CANCELLED, FAILED ou SUCCEEDED, une exception sera levée. Cette méthode ne doit être invoquée que depuis le JavaFX Application Thread.

Une fois ceci fait, il est à nouveau possible d'invoquer la méthode start() du service.

Il est également possible d'invoquer la méthode de convenance restart().

Par exemple :

Code Java : Sélectionner tout
service.restart();

Ce qui aura pour effet de redémarrer complètement le service. Cette méthode ne doit être invoquée que depuis le JavaFX Application Thread.

Invoquer cette méthode est l’équivalent d'invoquer successivement :

Code Java : Sélectionner tout
1
2
3
service.cancel(); 
service.reset(); 
service.start();

Mis à jour le 2 octobre 2014 bouye

Il est possible d'envoyer des notifications durant un traitement long. La classe Task<V> dispose de quatre méthodes destinées à cet effet, et qui peuvent être invoquées directement depuis le code s’exécutant dans la méthode call() de la tâche :

  • updateMessage() - permet de faire remonter un message textuel ;
  • updateProgress() - permet d'indiquer l’état d'avancement du traitement. Cette méthode prend deux valeurs en paramètres, l'avancement actuel et l'avancement total. Un avancement négatif ou invalide (infinité ou NaN) résultera en une valeur de progression indéterminée ;
  • updateTitle() - permet de modifier l’intitulé du traitement ;
  • updateValue() - permet de modifier la valeur de retour de la tâche pour spécifier une valeur intermédiaire - JDK8 ou ultérieur.


Invoquer ces méthodes permet de mettre à jour de manière asynchrone les propriétés équivalentes message, progress, title et value de la tâche et de son service parent. Pour éviter une gestion d’événement trop lourde, ces méthodes fusionnent les appels en cas d'invocations successives trop rapprochées pour ne conserver que la dernière valeur : certaines valeurs intermédiaires peuvent donc être omises et ne pas être répercutées sur les propriétés.

  • message - le dernier message posté.
  • progress - le dernier état d'avancement du traitement. Cette valeur est initialement à 0. Une valeur de 1 indique normalement que le traitement est terminé. Une valeur de -1 indique que l'avancement est indéterminé.
  • title - le dernier intitulé du traitement.
  • value - la dernière valeur du traitement. Cette propriété sera également affectée à la valeur de retour de la tâche à la fin du traitement.


Il est donc possible de construire des IU permettant de suivre l'avancement d'un traitement en effectuant du binding sur ces propriétés.

Par exemple :

Code Java : Sélectionner tout
1
2
3
titleLabel.textProperty().bind(service.titleProperty()); 
progressLabel.textProperty().bind(service.messageProperty()); 
progressBar.progressProperty().bind(service.progressProperty());

Ici, les labels contenant l'intitulé et le message du traitement, de même que la barre de progression se mettront automatiquement à jour.

Mis à jour le 2 octobre 2014 bouye

Pour récupérer le résultat final d'un service, il suffit de récupérer la valeur de sa propriété en lecture seule value lorsque le service est dans l’état SUCCEEDED.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
service.setOnSucceeded(workerStateEvent -> { 
    final V result = service.getValue(); 
    System.out.printf("Traitement terminé %s !", result).println(); 
});

Mis à jour le 2 octobre 2014 bouye

Pour connaitre les causes de l’échec d'un service, il faut récupérer la valeur de sa propriété en lecture seule exception lorsque le service est dans l’état FAILED. Cette propriété contient une valeur de type java.lang.Throwable.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
service.setOnFailed(workerStateEvent -> { 
     System.err.println("Échec du service !"); 
     service.getException().printStackTrace(); 
});

Mis à jour le 2 octobre 2014 bouye

Depuis le JDK 8, pour créer une tâche de fond répétable, depuis notre IU (par exemple en réponse à un clic sur un bouton), nous allons créer une instance de la classe abstraite ScheduledService<V> avec le type de retour approprié. Nous allons ensuite surcharger la méthode createTask() de manière à initialiser une instance de la classe abstraite Task<V>. Dans cette tâche, nous allons surcharger la méthode call() pour effectuer le traitement de longue durée. Le résultat du traitement sera retourné en fin de méthode.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final ScheduledService<Integer> service = new ScheduledService<Integer>() { 
  
    @Override 
    protected Task<Integer> createTask() { 
        return new Task<Integer>() { 
  
            @Override 
            protected Integer call() throws Exception { 
                int result = 0; 
                // Faire le traitement ici. 
                return result; 
            }                             
        }; 
    }                     
};

Ici, nous avons créé un service et une tâche qui produisent une valeur entière.

Dans le cas où nous ne produisons rien, il faut procéder comme suit :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
final ScheduledService<Void> service = new ScheduledService<Void>() { 
  
    @Override 
    protected Task<Void> createTask() { 
        return new Task<Void>() { 
  
            @Override 
            protected Void call() throws Exception { 
                // Faire le traitement ici. 
                return null; 
            }                             
        }; 
    }                     
};

Ici, null est la seule valeur utilisable avec le type java.lang.Void.

Un ScheduledService se crée et se manipule donc de manière similaire à un Service simple. Cependant il y a une différence majeure : un Service s’exécute une seule et unique fois (mais peut être relancé ultérieurement) tandis qu'un ScheduledService se répète automatiquement après un délai lors que son exécution a été couronnée de succès. Le programmeur peut également contrôler si le service est autorisé à se relancer en cas d’échec. Par défaut, un service répétable s’arrête lorsque son exécution échoue.

Mis à jour le 15 mars 2015 bouye

Pour différer le lancement d'une tâche de fond répétable, vous devez spécifier une valeur de type javafx.util.Duration dans la propriété delay de votre instance de la classe ScheduledService<V>.

Par exemple :

Code Java : Sélectionner tout
1
2
service.setDelay(Duration.seconds(5)); 
service.start();

Ici, le service démarrera après cinq secondes d'attente.

Mis à jour le 15 mars 2015 bouye

Pour autoriser une tâche de fond répétable à se relancer en cas d’échec, vous devez modifier la valeur de la propriété restartOnFailure de l'instance de la classe ScheduledService<V> et lui donner la valeur true.

Par exemple :

Code Java : Sélectionner tout
service.setRestartOnFailure(true);

Ici, le service se répètera même en cas d’échec.

Mis à jour le 15 mars 2015 bouye

Pour limiter le nombre de répétitions d'une tâche de fond répétable en cas d’échec, vous devez modifier la valeur de sa propriété maxFailureCount.

Par exemple :

Code Java : Sélectionner tout
service.setMaxFailureCount(100);

Ici, notre service s’arrêtera de lui-même après le centième échec.

Mis à jour le 15 mars 2015 bouye

Pour connaitre le nombre d’échecs d'une tâche de fond répétable, vous pouvez interroger sa propriété en lecture seule currentFailureCount. Cette propriété contient le nombre d’échecs du service depuis son lancement initial. Ce nombre sera réinitialisé à zéro si le service est manuellement redémarré.

Par exemple :

Code Java : Sélectionner tout
final int failureNumber = service.getCurrentFailureCount();

Mis à jour le 15 mars 2015 bouye

Pour spécifier la période d'une tâche de fond répétable, vous devez spécifier une valeur de type javafx.util.Duration dans la propriété period de votre instance de la classe ScheduledService<V>. Il s'agit de la période de temps qui s’écoule entre deux moments durant lesquels le service passe dans un état RUNNING lors d'une exécution couronnée de succès.

Code Java : Sélectionner tout
service.setPeriod(Duration.seconds(1));

Ici, nous avons indiqué que notre service doit se répéter toutes les secondes.

Le service ne peut interrompre une tâche en cours, donc cette dernière continuera de s’exécuter même si le temps spécifié dans la période est dépassé. Si la tâche prend plus de temps que la période à s’exécuter ou si la période est vide (Duration.ZERO ou durée non définie), le service s’exécutera à nouveau immédiatement après que la tâche se soit terminée sans attendre.

En cas d’échec du service, il est possible d'ajouter un temps d'attente supplémentaire en fournissant une fabrique à durées, de type Callback<ScheduledService<?>, Duration> dans sa propriété backoffStrategy. Cette fabrique n'est invoquée que lorsque l’exécution du service échoue. Cela permet, par exemple, d'augmenter le temps d'attente entre chaque tentative de connexion qui n'est pas couronnée de succès :

Code Java : Sélectionner tout
1
2
3
4
5
6
service.setBackoffStrategy(new Callback<ScheduledService<?>, Duration>() { 
  
    public Duration call(ScheduledService<?> service) { 
        return Duration.seconds(service.getCurrentFailureCount() * 5); 
    } 
});

Ou :

Code Java : Sélectionner tout
service.setBackoffStrategy(service -> Duration.seconds(service.getCurrentFailureCount() * 5));

Ici, pour chaque exécution qui a échoué, le service attendra cinq secondes supplémentaires avant de tenter à nouveau de s’exécuter.

Le temps spécifié dans la propriété period et le temps additionnel en cas d’échec (qui est de zéro en cas d’exécution correcte) forment la période cumulée qui est le temps effectif entre chaque passage du service dans l’état RUNNING. Cette valeur peut être consultée via la propriété en lecture seule cumulativePeriod.

Il est possible de plafonner la période cumulée en spécifiant une valeur de type Duration dans la propriété maximumCumulativePeriod.

Par exemple :

Code Java : Sélectionner tout
service.setMaximumCumulativePeriod(Duration.minutes(10));

Ici, la période cumulée sera d'au maximum 10 minutes.

Mis à jour le 15 mars 2015 bouye

Lorsqu'une tâche de fond répétable se relance, sa propriété value est réinitialisée avec la valeur null. Cependant la propriété en lecture seule lastValue conserve le résultat de la dernière bonne exécution du service.

Par exemple :

Code Java : Sélectionner tout
final Integer lastResult = service.getLastValue();

Avant le premier lancement du service, cette propriété contient la valeur null.

Mis à jour le 15 mars 2015 bouye

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 -