IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Methode DoInBackground() de SwingWorker<Integer, String>


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 17
    Par défaut Methode DoInBackground() de SwingWorker<Integer, String>
    Bonjour,
    Voila je me permets de vous poster un message, car j'ai besoin de votre aide sur l'utilisation de la méthode doInBackground(). En effet, j'ai une application qui permets d'ouvrir un fichier .xml à l'aide d'une fenetre (JFileChooser).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	private void choixFichier() throws SecurityException, IOException {
    		fenetreChoix = new JFileChooser(System.getProperty("user.dir"));
    		fenetreChoix.setFileSelectionMode(JFileChooser.FILES_ONLY);
    		filtre = new ExtensionFileFilter("XML (*.xml)", "XML");
    	    fenetreChoix.setFileFilter(filtre);
    	    fenetreChoix.setAcceptAllFileFilterUsed(false);	// Permet d'enlever "Tous les fichiers"
    	    if (fenetreChoix.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
    	    		execute();
    	    	}
    	    }
    Et la méthode doInBackground(), ci dessous :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	protected Integer doInBackground() throws Exception {
    		System.out.println("ici");
        	File file = fenetreChoix.getSelectedFile();
        	System.out.println(file.getAbsolutePath());
    	   	if(getType(file)){
    			String chemin = file.getAbsolutePath();
    	        model.setParseur(new File(chemin));
    	        JTree temp = new JTree(model.buildTree());
    	    	affichage.setArbre(temp.getModel());
        	}
    	   	new Logging(affichage).ajouter("Ouverture du fichier effectuée");
    		return null;
    	}
    Mon problème vient du fait que lors de l'ouverture du premier fichier xml. J'entre bien dans la méthode choixFichier() et j'exécute bien la méthode execute() qui permets d'appelerla méthode doInBackground().
    Ce n'est lors d'une deuxième ouverture successive que la méthode appelée par execute() ne se fait plus. Savez vous pourquoi?

    Merci d'avance,

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    42
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 42
    Par défaut
    Bonjour,

    Trois petites choses avec ton SwingWorker :

    • le code que tu montres n'est pas complet, mais de ce qu'on peut en voir, tu sembles tenter de toujours utiliser la même instance de SwingWorker, or la JavaDoc précise bien qu'une instance de cette classe est à usage unique ; une fois exécuté, ton SwingWorker ne sert plus à rien, et une nouvelle instance doit être créée, ce qui explique ta méthode execute() qui ne fait rien la deuxième fois
    • le principe d'un SwingWorker, c'est de pouvoir exécuter du code consommateur en temps en arrière-plan, sans gêner l'exécution de la boucle de messages graphiques, qui tourne dans l'EDT (Event Dispatching Thread) ; or ici tu appelles dans ta méthode doInBackground() (qui tourne dans le thread à part donc) du code graphique, c'est-à-dire que tu le fais hors de l'EDT, ce qui est dangereux car Swing n'est pas thread-safe
    • enfin, si ton SwingWorker ne doit jamais rien renvoyer (cf. le "return null"), il est inutile de le déclarer comme SwingWorker<Integer, String>, SwingWorker<Void, String> fait l'affaire (mais là ça dépend de la suite de ton code)


    J'ai eu beaucoup de mal moi aussi à me familiariser avec le fonctionnement des SwingWorker, mais c'est très simple quand on y pense. Si tu souhaites un bref topo là-dessus, je serai ravi de développer.

    A+

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 17
    Par défaut
    * Alors pour ton premier point en effet j'essayais toujours d'utiliser la même instance. C'est donc pour cela que ca ne fonctionnait que la 1ere fois.

    * Pour la 2e point, en effet j'ai retravaillé cela. J'utilise maintenant le SwingWorker dans ma classe construisant mon arbre donc du coup, il n'agit pas sur le graphique

    Donc du coup mon code correspond à peu près à cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    if(getType(file)){
            String chemin = file.getAbsolutePath();
    	model.setParseur(new File(chemin));
    	JTree temp = new JTree(model.buildTree());
    	affichage.setArbre(temp.getModel());
    }
    new Logging(affichage).ajouter("Ouverture du fichier effectuée");
    ** C'est donc ma classe Parseur que je vais devoir faire étendre de SwingWorker. L'objet de type Parseur est model ici.
    Ma logique serait donc dans ma classe Parseur, de mettre le code de la méthode buildTree(), dans la méthode doInBackground(), ce qui permettrait de construire l'arbre en arrière plan. C'est ça qui peut prendre du temps en fonction du fichier xml chargé.

    ** Il me faudrait donc dans la partie de code ci dessus, faire un model.execute() ce qui me permettrait de lancer en arrière plan la construction de l'arbre. Mon seul soucis est que pour mettre à jour mon modèle il me faut récupérer un DefaultMutableTreeNode, pour cela il faudrait donc que la méthode doInBackground() me retourne cela? Mais comment puis je me débrouiller pour récupérer ce nouvel arbre crée car auparavant c'etait ma méthode builtTree() qui me le renvoyait...

    Merci de tes confirmations. Si tu as besoin de plus de code pour bien visualiser ce que je te dis, n'hésites pas :p


    Edit :
    J'avais tapé quelquechose dans ce genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
     if (fenetreChoix.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
        	File file = fenetreChoix.getSelectedFile();
       	if(getType(file)){
    		String chemin = file.getAbsolutePath();
    		model.setParseur(new File(chemin));
    		model.execute();
    	        JTree temp = new JTree(model.getArbre());
    	    	affichage.setArbre(temp.getModel());
        	}
    	   	new Logging(affichage).ajouter("Ouverture du fichier effectuée");
    }
    J'ai donc dans ma classe Parseur quelquechose comme cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    public void setParseur(File file) throws ParserConfigurationException, SAXException, IOException{
    	factory = DocumentBuilderFactory.newInstance();
    	builder = factory.newDocumentBuilder();
    	doc = builder.parse(file);
    	this.root = doc.getDocumentElement();
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    @Override
    protected DefaultMutableTreeNode doInBackground(){
    	Node firstNode = this.doc.getFirstChild();
    	DefaultMutableTreeNode branche = new DefaultMutableTreeNode(firstNode.getNodeName());
    	return listTree(firstNode,branche);
    }
    J'ai donc essayé de faire une methode getArbre() qui me retournerait l'appel à la méthode doInBackground() mais sans succès car ce n'est pas le but du threads en arrière plan.

    Peux tu me dire si je suis sur la bonne piste, et si possible ce qui peut me manquer pour récuperer mon arbre ^^

    merci

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 17
    Par défaut
    Je mets mon avancement sur le sujet.
    J'ai peu avancé depuis hier soir, mais bon...
    J'ai peux donc récupéré mon arbre avec la méthode get();

    Je crois qu'au niveau mise en place du code tout se trouve au bon endroit. Mon seul soucis reste l'appel possible successivement de la méthode execute() qui ne fonctionne qu'une fois.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    if(getType(file)){
    	String chemin = file.getAbsolutePath();
    	model.setParseur(new File(chemin));
    	System.out.println("ici");
    	model.execute();
    	System.out.println("ici3");
            JTree temp = new JTree(model.get());
        	affichage.setArbre(temp.getModel());
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    @Override
    protected DefaultMutableTreeNode doInBackground(){
           	System.out.println("ici2");
    	Node firstNode = this.doc.getFirstChild();
    	DefaultMutableTreeNode branche = new DefaultMutableTreeNode(firstNode.getNodeName());
    	return listTree(firstNode,branche);
    }

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    42
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 42
    Par défaut
    Salut,

    Alors en fait, la vraie question, c'est de déterminer précisément ce que tu veux exécuter en arrière-plan. Si j'ai bien compris, ce que tu considères comme consommateur en temps et que tu veux isoler, c'est la lecture d'un XML et la construction d'un arbre. S'il est vrai que l'analyse XML peut s'avérer un peu longue, la construction de l'arbre devrait par contre être suffisamment peu consommatrice pour être faite dans l'EDT. Mais après tout, c'est possible aussi, vu que la construction de l'arbre ne consiste qu'à créer des DefaultMutableTreeNode si j'ai bien compris. Le mieux serait d'essayer les deux (construction de l'arbre dans puis hors de l'EDT) et de voir s'il y a une différence. S'il n'y en a pas, le mieux est de la laisser dans l'EDT.

    Pour ce qui est de récupérer ton noeud racine à l'issue de la construction, c'est en effet avec la méthode get() que tu peux le faire, sauf que dans le code que tu as mis, tu l'utilises mal. Si on part sur une construction d'arbre hors de l'EDT, comme tu l'as fait, voici comment je ferais (j'explique plus en détail après).

    • Il faut commencer par créer une classe qui hérite de SwingWorker<DefaultMutableTreeNode, Void> :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
       
      public class TreeBuilderSwingWorker extends SwingWorker<DefaultMutableTreeNode, Void> {
        private File file;
       
        public TreeBuilderSwingWorker(File file) {
          this.file = file;
        }
       
        @Override
        protected DefaultMutableTreeNode doInBackground() throws Exception {
          Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
          Element root = doc.getDocumentElement();
       
          // [... Code qui génère l'arbre ...]
       
          return rootTreeNode; // Où rootTreeNode est le DefaultMutableTreeNode racine.
        }
      }
      Voilà pour le SwingWorker. Il devrait se limiter à ça.
    • Il faut ensuite exécuter le SwingWorker :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
       
      if(getType(file)) {
              new TreeBuilderSwingWorker(file) {
                @Override
                public void done() {
                  try {
                    JTree temp = new JTree(model.get());
          	      affichage.setArbre(temp.getModel()); // Attention, affichage doit être déclaré final pour que cette ligne compile.
                  }
                  catch(ExecutionException exception) {
                    // Gestion de l'exception qui s'est produite dans doInbackground(), et qui se récupère avec exception.getCause().
                  }
                  catch(Exception exception) {
                    // Gestion des autres exceptions inhérentes à la manipulation des threads.
                  }
                }
              }.execute();
      }


    Je n'ai pas testé ces bouts de code, mais ils devraient fonctionner et résoudre tous tes problèmes. On créé bien une nouvelle instance du worker à chaque exécution, ce qui règle ton souci principal. On exécute la lecture du XML et la fabrication de l'arbre en arrière-plan, ce qui est bien ce qu'on veut. Et côté graphique, on effectue le travail final dans l'EDT une fois que le worker a terminé, à l'aide de la méthode done().

    Quelques petites explications :
    • On passe au TreeBuilderSwingWorker un File à la construction, pour qu'il ait tout ce qu'il lui faut lors de l'exécution de doInBackground(). A ce sujet, une petite remarque :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
       
      String chemin = file.getAbsolutePath();
      model.setParseur(new File(chemin));
      peut être résumé en :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
       
      model.setParseur(file);
      En effet, il est parfaitement inutile de récupérer le chemin absolu du fichier, et d'en recréer une instance à partir de cela. Mais comme tu l'as vu, ces lignes n'existent plus dans le code que je t'ai proposé.
    • Le code qui doit s'exécuter en arrière-plan est isolé dans cette fameuse classe TreeBuilderSwingWorker, qui devait plus ou moins correspondre à ta classe Parseur je pense. Tu auras remarqué que je n'ai écrit dans cette classe que ce code-là.
    • Le code graphique qui doit s'exécuter (dans l'EDT donc) une fois le traitement terminé doit être mis dans la méthode done() du SwingWorker. Je n'ai pas écrit cette méthode directement dans la classe TreeBuilderSwingWorker car ce code est purement graphique et dépend complètement de ton interface, que le worker ne connaît pas. C'est pourquoi je redéfinis done() à la volée et au dernier moment, à l'aide d'une classe anonyme. Si on avait voulu définir done() directement dans la classe, il aurait fallu lui passer à la construction ton objet affichage, mais c'est beaucoup moins propre.
      Alors ici, une remarque très importante ; comme je le disais au début, tu as mal utilisé la méthode get(). En effet, cette méthode doit te renvoyer le résultat du traitement en arrière-plan, mais elle ne peut évidemment pas le faire si ce traitement n'est pas encore terminé ! Du coup, si tu l'appelles comme tu le faisais, juste après le execute(), l'exécution va être bloquée jusqu'à ce que doInBackground() soit terminée. Et du coup, le traitement en arrière-plan ne sert plus à rien ! Tu lances l'exécution d'un traitement en arrière-plan, et la ligne d'après tu bloques tout en attendant que ce soit fini. Du coup, c'est comme si tu avais directement collé le code de doInBackground() à l'emplacement du execute().
    • Pour ce qui est des exceptions, ça fonctionne comme pour le résultat du traitement : tout est différé jusqu'au moment où tu appelles get(). C'est pour ça que doInBackground() déclare dans sa signature "throws Exception". Au moment où tu appelles get(), tu peux récupérer le retour de doInBackground(), mais tu vas aussi récupérer l'éventuelle exception qui s'est produite pendant son exécution (elle sera encapsulée dans une ExecutionException et pourra être récupérée avec getCause()), ou encore une exception liée à un problème de concurrence (si par exemple tu appelais interrupt() sur le thread du worker).


    Si je peux me permettre une dernière remarque, ces deux lignes m'intriguent :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    JTree temp = new JTree(model.buildTree());
    affichage.setArbre(temp.getModel());
    Visiblement, ton objet affichage utilise un TreeModel pour travailler, je suppose que c'est pour créer un JTree. Pourquoi ne pas directement lui passer temp ? Parce que là, tu créé un arbre pour récupérer son modèle, puis tu laisses tomber l'arbre et tu en créé un nouveau.

    En espérant t'avoir aidé.

    A+

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 01/10/2014, 09h37
  2. Réponses: 1
    Dernier message: 16/08/2012, 21h49
  3. Différence Integer/String quand VarType = 8
    Par linkcr15 dans le forum VB.NET
    Réponses: 4
    Dernier message: 27/04/2012, 18h13
  4. Conversion integer <-> string
    Par emileprosky dans le forum Pascal
    Réponses: 6
    Dernier message: 20/04/2008, 23h31
  5. [split]methode non définie pour le type string
    Par maniolo dans le forum Langage
    Réponses: 12
    Dernier message: 12/04/2006, 16h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo