2010-02-26 5 views
4

J'essaie de mettre en œuvre un PassiveView basé système gui en balançoire. Fondamentalement, je veux garder mon implémentation de vue (la partie qui contient réellement le code de swing) minimale, et faire la plupart du travail dans ma classe Presenter. Le Présentateur ne devrait pas avoir de dépendance vis-à-vis du swing et devrait également "lancer le show", c'est-à-dire dire à la vue quoi faire et non vice versa.Swing, Passive View et tâches de longue durée

Je rencontre des problèmes lorsque je fais face à des tâches de longue durée, et la séparation des threads en général. Je souhaite que les mises à jour de l'interface graphique s'exécutent sur l'EDT et que la logique du présentateur s'exécute sur un thread différent. Si je veux le présentateur de mettre à jour une partie de l'interface graphique, il est assez facile, j'écris quelque chose comme ça:

public interface View { 
    void setText(String text); 
} 

public class Presenter { 
    View view; 
    ... 
    public void setTextInVIew() { 
     view.setText("abc"); 
    } 
} 

public class MyFrame implements View { 
    JTextField textField; 
    ... 
    public void setText(final String text) { 
     SwingUtilities.InvokeLater(new Runnable() { 
      public void run() { 
       textField.setText(text); 
      } 
     }); 
    } 
} 

Toutefois, lorsque l'interface graphique est d'informer le présentateur qu'une action a eu lieu, je veux ÉlimiMercure de l'EDT en réagir dans un autre thread:

public class Presenter { 
    ... 
    public void buttonPressed() { 
     // shouldn't run on EDT 
    } 
} 

public class MyFrame implements View { 
    JButton button; 
    public MyFrame() { 
     ... 
     button.addActionListener(new ActionListener() { 
      @Override public void actionPerformed(ActionEvent e) { 
       presenter.ButtonPressed(); 
      } 
     }); 
    } 
} 

puisque le code actionPerformed est en cours d'exécution de l'EDT, il en sera le presenter.buttonPressed. Je sais que swing a le concept de SwingWorker - exécuter des tâches dans un thread différent, mais il semblerait que je devrais insérer du code swing dans mon présentateur, et la vue est en cours d'exécution du spectacle. Des idées pour résoudre ceci?

Répondre

2

vous pourriez faire quelque chose comme ce qui suit, qui gardera votre code GUI en place et il suffit d'effectuer le travail pour sortir de l'EDT:

button.addActionListener(new ActionListener() { 
     @Override public void actionPerformed(ActionEvent e) { 
      SwingWorker sw = new SwingWorker() { 
      public Object doInBackground(){ 
       presenter.ButtonPressed();    
       return null; 
      } 
      }; 
      sw.execute(); 
     } 
    }); 
+0

J'utilise un modèle similaire à celui ci-dessus, et les threads SwingWorker fonctionnent comme prévu. +1 – Luhar

+0

En guise d'optimisation, je recommande d'utiliser un exécuteur au lieu de générer un nouveau thread pour chaque appel de travailleur. –

2

Vous pourriez être intéressé par le Task API pour éviter tout passe-partout. Dans le cas contraire, la solution d'akf semble correcte (bien qu'il ne soit pas nécessaire de créer une variable pour SwingWorker, vous pouvez juste en créer une nouvelle et en exécuter une anonyme).

+0

Votre méthode doInBackground() doit renvoyer Void (pas void) et vous devez donc renvoyer quelque chose (par exemple null) après l'appel à presenter.ButtonPressed(). – Adamski

+0

@Adamski: vous avez raison, retiré. – JRL

+0

JRL: Vous étiez toujours une ligne plus courte que akf que vous appelez .execute() plutôt que d'assigner SwingWorker à une variable locale. – Adamski

0

Une autre approche de la solution SwingWorker décrite par d'autres consiste à utiliser un bus d'événements avec une affinité de thread. Je pense réellement que cela pourrait être la meilleure option pour le type de découplage que vous allez pour.

Départ: EventBus

il y a d'autres implémentations de l'architecture de bus, mais EventBus est populaire.

- mise à jour -

Alors EventBus va fournir un moyen très propre de mandatement de non-EDT à EDT (beaucoup plus agréable que des tonnes d'appels explicites à SwingUtilities.invokeLater() - mais essentiellement faire la même chose: même si un EventBus est capable de regrouper de nombreuses notifications et de les faire apparaître dans une seule machine exécutable EDT, les performances seront meilleures). Mais cela ne répond pas à la nécessité de proxy des événements de l'EDT et de les faire fonctionner sur un thread de travail. Il existe une classe ThreadSafeEventService dans EventBus qui pourrait probablement être utilisée comme base pour une telle bête - elle pourrait être couplée avec un ExecutorService, par exemple, pour traiter certains enregistrements d'événements pour certains auditeurs.

Je pense que la clé de tout cela pour moi est que quelle que soit la solution que vous venez avec, il faut essayer de résumer le filage marche/arrêt EDT

BTW - Ce que vous demandez ici est vraiment semblable à Le modèle de thread de Microsoft Apartment.

+0

devez-vous explicitement faire quelque chose pour obtenir cette affinité de thread? J'ai suivi le code de démarrage d'EventBus (le Frame publie changeEvent sur un clic de bouton, et le Presenter s'abonne à cet événement), cependant le traitement de l'événement dans le Presenter est toujours fait sur l'EDT –

+0

c'est un bon point. EventBus effectue uniquement un proxy de manière unilatérale (si un événement provient de l'EDT, il sera automatiquement transmis à l'EDT). Il semble que vous en ayez besoin pour travailler dans les deux sens (les événements provenant de l'EDT doivent être transmis aux auditeurs à partir de l'EDT). Je vais mettre à jour le post pour développer un peu. –

0

OK - j'ai une autre option pour vous: Spin

En fin de compte, toutes ces solutions sont mandatement des appels entre les threads. Je pense que le but est de trouver une solution qui ne nécessite pas de code de passe-partout de votre côté. Vous pouvez, par exemple, câbler tous vos écouteurs afin qu'ils vérifient s'ils sont sur un thread de travail approprié, puis procèdent à un service ExecutorService si ce n'est pas le cas. Mais c'est un problème majeur. Il est préférable d'obtenir ce proxy se produisant dans la couche entre votre entreprise et afficher des objets - le liant/écouteur/tout ce que vous voulez appeler couche.

Questions connexes