Swing,Passive View和长时间运行的任务

时间:2010-02-26 13:09:07

标签: java multithreading design-patterns swing passive-view

我正在努力实现基于Passive View的gui系统。基本上我想保持我的视图实现(实际包含swing代码的部分)最小化,并在我的Presenter类中完成大部分工作。演示者应该不依赖于摇摆,也应该“运行节目”,即告诉视图该做什么而不是反过来。

在处理长时间运行的任务时遇到问题,而且一般情况下线程分离。我希望GUI更新在EDT上运行,并且演示者逻辑在不同的线程上运行。如果我希望演示者更新GUI的某些部分,这很容易,我写的是这样的:

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);
            }
        });
    }
}

但是,当GUI要告知演示者某些动作已经发生时,我想在不同的线程中切换出EDT对它的反应:

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();
            }
        });
    }
}

因为actionPerformed代码是从EDT运行的,所以presenter.buttonPressed也是如此。我知道swing有SwingWorker的概念 - 在不同的线程中运行任务,但是看起来我必须将swing代码插入到我的演示者中,并且视图正在运行show。任何想法如何解决这个问题?

4 个答案:

答案 0 :(得分:2)

您可以执行以下操作,这将保留您的GUI代码并简单地执行工作以退出EDT:

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

答案 1 :(得分:2)

您可能对Task API感兴趣以避免所有样板。否则,akf的解决方案看起来很好(虽然不需要为SwingWorker创建变量,但您可以只创建一个匿名变量)。

答案 2 :(得分:0)

其他人概述的SwingWorker解决方案的另一种方法是使用具有线程亲和力的事件总线。我实际上认为这可能是您正在寻找的解耦类型的最佳选择。

退房:EventBus

总线架构还有其他实现,但EventBus很受欢迎。

- 更新 -

所以EventBus将提供一种从非EDT到EDT的非常干净的代理方式(比大量的SwingUtilities.invokeLater()显式调用好很多 - 但基本上做同样的事情。虽然EventBus能够捆绑了许多通知并让它们在单个EDT可运行中命中,因此性能会更好。)

但是这并没有解决从EDT代理事件并让它们在工作线程上运行的需要。 EventBus中有一个ThreadSafeEventService类,它可以用作这种野兽的基础 - 它可以与ExecutorService结合,例如,为某些监听器处理某些事件注册。

我想这对我来说关键在于无论你提出什么解决方案,它都应该尝试将旋转封装在EDT上/关闭

BTW - 你在这里询问的内容与微软的公寓线程模型非常类似。

答案 3 :(得分:0)

好的 - 我还有另一种选择:Spin

最终,所有这些解决方案都是线程之间的代理调用。我认为我们的目标是找到一个不需要大量样板代码的解决方案。例如,您可以连接所有侦听器,以便检查它们是否在适当的工作线程上,然后代理到ExecutorService(如果不是)。但那是一个很大的麻烦。更好的做法是在业务和视图对象之间的层中进行代理 - 绑定/监听器/你想要称之为层的任何内容。