Utilisation du mode de filetage Swingworker
Il est très important pour les développeurs de swing d'utiliser soigneusement la concurrence. Un bon programme swing utilise des mécanismes de concurrence pour créer des interfaces utilisateur qui ne perdent pas de réponse - quel que soit le type d'interaction utilisateur, le programme peut toujours y répondre. Pour créer un programme réactif, les développeurs doivent apprendre à utiliser le multithreading dans le cadre de swing.
Un développeur swing s'occupera des types de fils suivants:
(1) Threads initiaux, ces threads exécuteront le code d'application d'initialisation.
(2) Le thread de répartition des événements, tous les codes de traitement d'événements sont exécutés ici. La plupart des code qui interagissent avec le cadre de swing doivent également exécuter ce thread.
(3) Les fils de travail, également appelés threads d'arrière-plan, effectueront toutes les tâches de temps.
Les développeurs n'ont pas besoin de créer ces threads explicitement dans leur code: ils sont fournis par le cadre d'exécution ou de swing. Le travail des développeurs est d'utiliser ces threads pour créer des programmes de swing réactifs et persistants.
Comme tous les autres programmes exécutés sur les plates-formes Java, un programme de swing peut créer des threads et des pools de threads supplémentaires, ce qui nécessite l'utilisation de l'approche qui sera introduite dans cet article. Cet article présentera les trois fils ci-dessus. La discussion sur les threads des travailleurs impliquera l'utilisation de la classe javax.swing.swingworker. Cette classe a de nombreuses fonctionnalités utiles, notamment la communication et la collaboration entre les tâches de threads de travail et d'autres tâches de fil.
1. Fil initial
Chaque programme génère une série de threads au début de la logique d'application. Dans un programme standard, il n'y a qu'un seul thread: ce fil appellera la méthode principale de la classe principale du programme. Dans une applet, le thread initial est un constructeur de l'objet Applet, qui appellera la méthode init; Ces actions peuvent être exécutées dans un seul thread, ou dans deux ou trois threads différents, tous en fonction de l'implémentation spécifique de la plate-forme Java. Dans cet article, nous appelons ce type de threads de threads initiaux.
Dans un programme de swing, il n'y a pas grand-chose à faire dans le fil initial. Leur tâche la plus élémentaire est de créer un objet exécutable qui initialise l'interface graphique et orchestre les objets utilisés pour exécuter des événements dans le thread de répartition des événements. Une fois l'interface graphique créée, le programme sera motivé principalement par des événements GUI, chacun provoquera l'exécution d'un événement dans le fil de répartition des événements. Le code du programme peut orchestrer des tâches supplémentaires aux threads pilotés par des événements (à condition qu'ils soient exécutés rapidement afin qu'ils n'interfèrent pas avec le traitement des événements) ou créent des threads de travail (utilisés pour effectuer des tâches longues).
Une tâche de création de GUI d'orchestration initiale consiste à appeler javax.swing.swingutilities.invokelater ou javax.swing.swingutilities.invokeandwait. Les deux méthodes prennent un paramètre unique: Runnable est utilisé pour définir de nouvelles tâches. La seule différence entre eux est: InvokerLater uniquement orchestre la tâche et les retours; Invokeandwait attendra l'exécution de la tâche avant de retourner.
Voir l'exemple suivant:
Swingutiability.invokelater (new Runnable ()) {public void run () {createAnDshowGui (); }} Dans Applet, la tâche de création de l'interface graphique doit être placée dans la méthode init et d'utiliser l'invokenadwait; Sinon, le processus initial sera possible avant la création de l'interface graphique, ce qui peut causer des problèmes. Dans d'autres cas, les tâches de création de l'interface graphique d'orchestration sont généralement les dernières du thread initial à exécuter, donc les deux utilisent invokelater ou invokeandwait.
Pourquoi le thread initial ne crée-t-il pas directement l'interface graphique? Parce que presque tout le code utilisé pour créer et interagir avec les composants de swing doit être exécuté dans le thread de répartition des événements. Cette contrainte sera discutée ci-dessous.
2. Fil de distribution d'événements
Le code de traitement de l'événement Swing est exécuté dans un thread spécial, qui s'appelle le thread de répartition des événements. La plupart du code qui appelle la méthode de swing est exécuté dans ce thread. Ceci est nécessaire car la plupart des objets swing sont "sans thread sûr".
Vous pouvez considérer l'exécution du code comme effectuant une série de tâches courtes dans le thread de répartition des événements. La plupart des tâches sont appelées par des méthodes de traitement des événements, telles que ActionListener.Actionperformed. Les tâches restantes seront orchestrées par le code du programme, en utilisant invokelater ou invokeandwait. Les tâches dans le thread de répartition des événements doivent pouvoir être exécutées rapidement. Sinon, les événements non transformés seront en arrière et l'interface utilisateur deviendra "réactive".
Si vous devez déterminer si votre code est exécuté dans le thread de répartition des événements, appelez javax.swing.swingutilities.iseventdispatchthread.
3. Fil de travail et swingworker
Lorsqu'un programme swing doit effectuer une longue tâche, cela se fera généralement à l'aide d'un fil de travail. Chaque tâche est exécutée dans un thread de travailleur, qui est une instance de la classe javax.swing.swingworker. La classe SwingWorker est une classe abstraite; Vous devez définir sa sous-classe pour créer un objet SwingWorker; Utilisez généralement des classes intérieures anonymes pour ce faire.
SwingWorker fournit des fonctionnalités de communication et de contrôle:
(1) La sous-classe de SwingWorker peut définir une méthode, effectuée. Une fois la tâche d'arrière-plan terminée, elle sera automatiquement appelée par le thread de répartition des événements.
(2) La classe SwingWorker implémente java.util.concurrent.future. Cette interface permet aux tâches d'arrière-plan de fournir une valeur de retour à d'autres threads. Les méthodes de cette interface fournissent également les fonctions qui permettent de se défaire des tâches d'arrière-plan et de déterminer si les tâches d'arrière-plan ont été effectuées ou révoquées.
(3) Les tâches d'arrière-plan peuvent fournir des résultats intermédiaires en appelant SwingWorker.publish, et le thread de répartition des événements appellera cette méthode.
(4) Les tâches d'arrière-plan peuvent définir les propriétés de liaison. Les modifications de l'attribut de liaison déclenchent des événements et le thread de répartition des événements appellera le gestionnaire d'événements pour gérer ces événements déclenchés.
4. Tâches backend simples
Voici un exemple, cette tâche est très simple, mais c'est une tâche potentiellement longue. L'applet TumbleItem importe une série de fichiers d'image. Si ces fichiers d'image sont importés via le thread initial, il y aura un retard avant que l'interface graphique n'apparaisse. Si ces fichiers d'image sont importés dans le thread de répartition des événements, l'interface graphique peut temporairement ne pas répondre.
Pour résoudre ces problèmes, la classe TumbleItem crée et exécute une instance de la classe StringWorker lorsqu'elle est initialisée. La méthode Doinbackground de cet objet est exécutée dans un thread de travailleur, importe l'image dans un tableau ImageIcon et lui renvoie une référence. Ensuite, la méthode Done est exécutée dans le thread de répartition des événements, obtenez la référence renvoyée et le placez dans les IMG de la variable membre de la classe Applet. Cela permet à la classe TumbleItem de créer une interface graphique immédiatement sans avoir à attendre que l'importation d'image se termine.
L'exemple de code suivant définit et implémente un objet SwingWorker.
Swingworker worker = new swingworker <ImageIcon [], void> () {@Override public ImageIcon [] doinbackground () {final ImageIcon [] innerimgs = new ImageIcon [NIMGS]; for (int i = 0; i <nimgs; i ++) {innerimgs [i] = loadImage (i + 1); } return innerimgs; } @Override public void Done () {// Supprime l'étiquette "Charging Images". animator.removeall (); LOOPSLOT = -1; essayez {imgs = get (); } catch (InterruptedException ignore) {} catch (java.util.concurrent.executionException e) {string why = null; Cause lancable = e.getCause (); if (cause! = null) {pourquoi = cause.getMessage (); } else {pourquoi = e.getMessage (); } System.err.println ("Fichier de récupération d'erreur:" + pourquoi); }}}}; Toutes les sous-classes héritées de SwingWorker doivent implémenter Doinbackground; La mise en œuvre de la méthode Done est facultative.
Notez que SwingWorker est une classe de paradigme avec deux paramètres. Le premier type de paramètre spécifie le type de retour de Doinbackground. Il s'agit également d'un type de méthode GET, qui peut être appelé par d'autres threads pour obtenir la valeur de retour de Doinbackground. Le deuxième type de paramètre spécifie le type du résultat intermédiaire. Cet exemple ne renvoie pas le résultat intermédiaire, il est donc défini sur vide.
En utilisant la méthode GET, vous pouvez faire référence aux IMG d'objet (créé dans le thread de travail) dans le thread de répartition des événements. Cela permet de partager des objets entre les threads.
Il y a en fait deux façons de faire retourner l'objet par la classe Doinbackground.
(1) Il n'y a pas de paramètres pour appeler SwingWorker.get. Si la tâche d'arrière-plan n'est pas terminée, la méthode GET se bloque jusqu'à ce qu'elle se termine.
(2) Appelez SwingWorker.get avec les paramètres pour spécifier le délai d'attente. Si la tâche d'arrière-plan n'est pas terminée, bloquez jusqu'à ce qu'elle se termine - sauf si le délai d'expiration expire, auquel cas GET lancera un java.util.concurrent.timeOutException.
5. Tâches avec des résultats intermédiaires
Il est utile d'avoir une tâche backend de travail fournissant des résultats intermédiaires. Les tâches backend peuvent appeler la méthode SwingWorker.publish pour ce faire. Cette méthode accepte de nombreux paramètres. Chaque paramètre doit être spécifié par le deuxième type de paramètre de SwingWorker.
Le SwingWorker.Process peut être remplacé pour enregistrer les résultats fournis par la méthode de publication. Cette méthode est appelée par le thread de répartition des événements. L'ensemble de résultats de la méthode de publication est généralement collecté par une méthode de processus.
Jetons un coup d'œil aux exemples fournis par Filpper.java. Ce programme génère une série de tests booléens aléatoires java.util.random via une tâche d'arrière-plan. C'est comme une expérience de lancement de pièces. Pour signaler ses résultats, la tâche d'arrière-plan utilise un objet Flippair.
classe statique privée Flippair {têtes longues finales privées, total; Flippair (têtes longues, long total) {this.heads = têtes; this.total = total; }} Les têtes représentent le résultat de True; Le total représente le nombre total de lancers.
Le programme d'arrière-plan est une instance de Filptask:
classe privée Fliptask étend SwingWorker <Void, Flippair> {
Étant donné que la tâche ne renvoie pas de résultat final, il n'est pas nécessaire de spécifier quel est le paramètre de premier type, utilisez void. Après chaque "lancer", les appels de tâche publient:
@OverRideProtected void DOINBACKROUND () {Long Heads = 0; Total long = 0; Aléatoire aléatoire = nouveau aléatoire (); while (! isCancelled ()) {total ++; if (random.nextBoolean ()) {Heads ++; } publier (nouveau Flippair (Heads, Total)); } retourner null;} Étant donné que la publication est souvent appelée, de nombreuses valeurs de flipp seront collectées avant que la méthode de processus ne soit appelée par le thread de répartition des événements; Le processus se concentre uniquement sur le dernier ensemble de valeurs renvoyés à chaque fois et l'utilise pour mettre à jour l'interface graphique:
Processus void protégé (list paires) {flippair paire = paires.get (paires.size () - 1); headstext.setText (string.format ("% d", pair.heads)); TotalText.SeTText (String.Format ("% D", pair.total)); DevText.SeTText (String.format ("%. 10G", ((double) paire.heads) / ((double) paire.total) - 0,5));} 6. Annuler la tâche d'arrière-plan
Appelez SwingWorker.Cancer pour annuler une tâche d'arrière-plan d'exécution. La tâche doit être cohérente avec son propre mécanisme d'annulation. Il y a deux façons de le faire:
(1) Lorsqu'une interruption est reçue, elle sera résiliée.
(2) Appelez Swingworker.isCanceled. Si SwingWorker appelle Annuler, la méthode reviendra True.
7. Bind Attributs and State Methods
SwingWorker prend en charge les propriétés liées, ce qui est très utile lors de la communication avec d'autres threads. Fournit deux propriétés de liaison: progrès et état. Les progrès et l'état peuvent être utilisés pour déclencher des tâches de traitement des événements dans les threads de répartition des événements.
En mettant en œuvre un écouteur de changement de propriété, le programme peut assurer des changements dans les progrès, l'état ou d'autres propriétés contraignantes.
7.1La variable liée aux progrès
La variable de liaison de progression est une variable entière avec une plage de 0 à 100. Il a prédéfini les méthodes de setter (le swingworker.setprogress protégé) et de Getter (les publics swingworker.getprogress).
7.2 La variable liée à l'état
Les changements dans les variables de liaison de l'état reflètent le processus de modification de l'objet SwingWorker pendant son cycle de vie. Cette variable contient un type d'énumération de swingworker.stateValue. Les valeurs possibles sont:
(1) en attente
Cet état dure un certain temps à savoir de la création de l'objet que la méthode Doinbackground est appelée.
(2) a commencé
Cet état dure un instant avant que la méthode Doinbackground ne soit appelée jusqu'à ce que la méthode terminée soit appelée.
(3) fait
Le temps restant de l'objet existera dans cet état.
Si vous devez renvoyer la valeur de l'état actuel, vous pouvez appeler SwingWorker.getState.
Méthodes 7.3status
Deux méthodes fournies par l'interface future peuvent également signaler l'état des tâches d'arrière-plan. Si la tâche est annulée, Iscancelled renvoie True. De plus, si la tâche est terminée, c'est-à-dire qu'elle est terminée normalement ou annulée, Isdone renvoie vrai.
En utilisant un conteneur de niveau supérieur
Swing fournit 3 classes de conteneurs de niveau supérieur: JFrame, JDialog, Japplet. Lorsque vous utilisez ces trois classes, vous devez prêter attention aux points suivants:
(1). Pour être affiché à l'écran, chaque composant GUI doit faire partie de la hiérarchie contenant. La hiérarchie d'inclusion est une structure d'arbre du composant, et le conteneur de niveau supérieur est sa racine.
(2). Chaque composant GUI ne peut être inclus qu'une seule fois. Si un composant est déjà dans un conteneur et tente ensuite de l'ajouter à un nouveau conteneur, le composant sera supprimé du premier conteneur et ajouté au deuxième conteneur.
(3). Chaque conteneur de niveau supérieur a un volet de contenu. Généralement, ce panneau de contenu contiendra (directement ou indirectement) tous les composants visuels de l'interface graphique de conteneur de niveau supérieur.
(4). Vous pouvez ajouter une barre de menu dans le conteneur supérieur. Habituellement, cette barre de menu est placée dans le conteneur supérieur, mais en dehors du panneau de contenu.
1. Conteneurs de haut niveau et hiérarchies d'inclusion
Chaque programme qui utilise le composant swing a au moins un conteneur de niveau supérieur. Ce conteneur de niveau supérieur est le nœud racine contenant la hiérarchie - cette hiérarchie contiendra tous les composants de swing qui apparaîtront dans ce conteneur de niveau supérieur.
En règle générale, une application basée sur l'interface graphique séparée a au moins une hiérarchie d'inclusion et son nœud racine est un JFrame. Par exemple, si une application a une fenêtre et deux boîtes de dialogue, l'application aura trois niveaux d'inclusion, c'est-à-dire qu'il y aura trois conteneurs de niveau supérieur. Une hiérarchie de confinement prend JFrame comme nœud racine, et deux autres hiérarchys de confinement ont chacun un JDialog comme nœud racine.
Une applet basée sur des composants de swing contient au moins une hiérarchie d'inclusion, et il peut être déterminé que l'un d'eux doit être fait avec un japplet comme nœud racine. Par exemple, une applet avec une boîte de dialogue, il aura deux niveaux d'inclusion. Le composant de la fenêtre du navigateur sera placé dans une hiérarchie de confinement, et son nœud racine est un objet Japplet. La boîte de dialogue aura une hiérarchie contenant et son nœud racine est un objet JDialog.
2. Ajouter des composants au panneau de contenu
L'opération de code suivante consiste à obtenir le panneau de contenu du cadre dans l'exemple ci-dessus et à ajouter une étiquette jaune:
frame.getContentPane (). Add (YellowLabel, BorderLayout.Center);
Comme indiqué dans le code, vous devez d'abord trouver le panneau de contenu du conteneur de niveau supérieur et l'implémenter via la méthode getContentPane. Le panneau de contenu par défaut est un simple conteneur intermédiaire, qui hérite de JComponent, en utilisant un borderlayout comme gestionnaire de panneaux.
La personnalisation d'un panneau de contenu est simple - configurez un gestionnaire de panneaux ou ajoutez des frontières. Il faut noter ici que la méthode GetContentPane renverra un objet de conteneur, pas un objet JComponent. Cela signifie que si vous devez profiter de certaines fonctionnalités de JComponent, vous devez également taper convertir la valeur de retour ou créer votre propre composant comme panneau de contenu. Notre exemple utilise généralement la deuxième méthode. Parce que la deuxième méthode est de plus en plus claire. Une autre façon dont nous utilisons parfois est d'ajouter simplement un composant autodéfini au panneau de contenu pour couvrir complètement le panneau de contenu.
Si vous créez votre propre panneau de contenu, veillez à vous assurer qu'il est opaque. Un jpanel opaque sera un bon choix. Notez que par défaut, la mise en page de JPanel est gérée comme FlowLayout, et vous voudrez peut-être le remplacer par un autre gestionnaire de mise en page.
Pour qu'un composant soit un panneau de contenu, vous devez utiliser la méthode SetContentPane du conteneur de niveau supérieur, par exemple:
// Créer un panneau et ajouter des composants à it.jpanel tentpane = new JPanel (new BorderLayout ()); contentPane.setBorder (Someborder); contentPane.add (SomeComponent, borderLayout.Center); en fait le contenu (autre Pane.//contentpane.setOpaque(True);toplevelContainer.setContentPane(ContentPane);
Remarque: N'utilisez pas de conteneurs transparents comme panneaux de contenu, tels que jscrollpane, jsplitpane et jtabbedpane. Un panneau de contenu transparent entraînera la confusion des composants. Bien que vous puissiez faire n'importe quel composant de swing transparent opaque via la méthode setopaque (vrai), il ne sera pas correct lorsque certains composants sont définis pour être complètement opaques. Par exemple, un panneau de balise.
3. Ajout d'une barre de menu
En théorie, chaque conteneur de niveau supérieur peut avoir une barre de menu. Mais les faits montrent que la barre de menu n'apparaît que dans le cadre ou l'applet. Pour ajouter une barre de menu dans le conteneur de niveau supérieur, vous devez créer un objet JMenubar, assembler des menus, puis appeler la méthode SetjMenubar. L'instance TopLevelDemo ajoute une barre de menu à son cadre via le code suivant.
frame.setJMenubar (CyanMenubar);
4. Le volet racine
Chaque conteneur de niveau supérieur repose sur un conteneur intermédiaire implicite appelé récipient racine. Ce conteneur racine gère le panneau de contenu et la barre de menu, ainsi que deux ou plusieurs autres conteneurs (voir le volet en couches, etc.). Vous n'avez généralement pas besoin de savoir l'utilisation du conteneur racine du composant swing. Cependant, si vous souhaitez intercepter un clic de souris ou dessiner sur plusieurs composants, vous devez connaître le conteneur racine.
Ce qui précède a été décrit sur le panneau de contenu et la barre de menu en option, et ne sera pas répété ici. Les deux autres composants contenus dans le récipient racine sont le panneau de disposition et le panneau de verre. Le panneau de mise en page contient directement le panneau de menu et le panneau de contenu et vous permet de trier les autres composants ajoutés par Z coordonnées. Les panneaux de verre sont généralement utilisés pour intercepter les actions d'entrée qui se produisent dans la couche supérieure et peuvent également être utilisées pour dessiner sur plusieurs composants.