我正在努力实现基于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。任何想法如何解决这个问题?
答案 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(如果不是)。但那是一个很大的麻烦。更好的做法是在业务和视图对象之间的层中进行代理 - 绑定/监听器/你想要称之为层的任何内容。