4

我正在尝试在摇摆中实现基于被动 视图的 gui 系统。基本上我想让我的视图实现(实际上包含摆动代码的部分)最小化,并在我的 Presenter 类中完成大部分工作。Presenter 应该不依赖于 swing,并且应该“运行节目”,即告诉视图要做什么,反之亦然。

在处理长时间运行的任务和线程分离时,我遇到了问题。我希望 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 代码插入到我的演示者中,并且视图正在运行节目。任何想法如何解决这个问题?

4

4 回答 4

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();
        }
    });
于 2010-02-26T13:24:54.347 回答
2

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

于 2010-02-26T13:58:03.157 回答
0

好的 - 我有另一个选择: 旋转

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

于 2010-02-28T04:56:12.333 回答
0

其他人概述的 SwingWorker 解决方案的另一种方法是使用具有线程关联性的事件总线。实际上,我认为这可能是您想要的解耦类型的最佳选择。

查看: EventBus

总线架构还有其他实现,但 EventBus 很流行。

- 更新 -

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

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

我想这一切对我来说的关键是,无论你想出什么解决方案,它都应该尝试封装 EDT 的开/关

顺便说一句-您在这里询问的内容与 Microsoft 的 Apartment 线程模型非常相似。

于 2010-02-27T05:31:06.083 回答