如何将SwingWorker的发布委托给其他方法

我的“问题”可以用以下描述。 假设我们有一个密集的过程,我们希望在后台运行并让它更新Swing JProgress栏。 解决方案很简单:

import java.util.List; import javax.swing.JOptionPane; import javax.swing.JProgressBar; import javax.swing.SwingWorker; /** * @author Savvas Dalkitsis */ public class Test { public static void main(String[] args) { final JProgressBar progressBar = new JProgressBar(0,99); SwingWorker w = new SwingWorker(){ @Override protected void process(List chunks) { progressBar.setValue(chunks.get(chunks.size()-1)); } @Override protected Void doInBackground() throws Exception { for (int i=0;i<100;i++) { publish(i); Thread.sleep(300); } return null; } }; w.execute(); JOptionPane.showOptionDialog(null, new Object[] { "Process", progressBar }, "Process", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); } } 

现在假设我有各种方法需要很长时间。 例如,我们有一个从服务器下载文件的方法。 或者另一个上传到服务器的。 或任何真的。 将发布方法委派给这些方法的正确方法是什么,以便他们可以适当地更新GUI?

到目前为止我发现的是(假设方法“aMethod”驻留在其他一些包中):

 import java.awt.event.ActionEvent; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JOptionPane; import javax.swing.JProgressBar; import javax.swing.SwingWorker; /** * @author Savvas Dalkitsis */ public class Test { public static void main(String[] args) { final JProgressBar progressBar = new JProgressBar(0,99); SwingWorker w = new SwingWorker(){ @Override protected void process(List chunks) { progressBar.setValue(chunks.get(chunks.size()-1)); } @SuppressWarnings("serial") @Override protected Void doInBackground() throws Exception { aMethod(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { publish((Integer)getValue("progress")); } }); return null; } }; w.execute(); JOptionPane.showOptionDialog(null, new Object[] { "Process", progressBar }, "Process", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); } public static void aMethod (Action action) { for (int i=0;i<100;i++) { action.putValue("progress", i); action.actionPerformed(null); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } } 

它有效,但我知道它缺乏一些东西。 有什么想法吗?

(我正在更新我的答案,使其更清晰和更全面)

虽然您已经成功地解耦了逻辑和表示,但它并没有以适合代码重用的方式完成。 Java的PropertyChangeSupport可以通过实现绑定属性轻松地将逻辑与表示分离,并获得一些实质性的重用。 我们的想法是使用事件处理程序而不是操作对象。

首先,概念化抽象。 后台工作需要间歇性地“喊出”(发布)到GUI,并且GUI需要监听它。 两个通用类将编写这个想法:

 /** * Wrapper for the background logic. * *  return type *  intermediary type (the "shout out") */ public static abstract class LoudCall implements Callable { private PropertyChangeSupport pcs; private S shout; public LoudCall() { pcs = new PropertyChangeSupport(this); } public void shoutOut(S s) { pcs.firePropertyChange("shoutOut", this.shout, this.shout = s); } public void addListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } public void removeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } @Override public abstract T call() throws Exception; } /** * Wrapper for the GUI listener. * *  return type *  intermediary type (the "shout out" to listen for) */ public static abstract class ListenerTask extends SwingWorker implements PropertyChangeListener { private LoudCall aMethod; public ListenerTask(LoudCall aMethod) { this.aMethod = aMethod; } @Override protected T doInBackground() throws Exception { aMethod.addListener(this); return aMethod.call(); } @Override public void propertyChange(PropertyChangeEvent evt) { if ("shoutOut".equals(evt.getPropertyName())) { publish((S)evt.getNewValue()); } } @Override protected abstract void process(List chunks); } 

这些类可用于所有Swing小部件。 对于ProgressBar,“shout out”将是一个Integer,返回类型为Void:

 public class ProgressExample { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // 1. setup the progress bar final JProgressBar progressBar = new JProgressBar(0, 99); // 2. Wrap the logic in a "Loud Call" LoudCall aMethod = new LoudCall() { @Override public Void call() throws Exception { for (int i = 0; i < 100; i++) { // "i have an update for the GUI!" shoutOut(i); Thread.sleep(100); } return null; } }; // 3. Run it with a "Listener Task" (new ListenerTask(aMethod) { @Override protected void process(List chunks) { progressBar.setValue(chunks.get(chunks.size() - 1)); } }).execute(); // 4. show it off! JOptionPane.showOptionDialog(null, new Object[] { "Process", progressBar }, "Process", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null ); } }); } } 

只有收听者需要了解有关GUI细节的任何信息,并且后台逻辑仍然可以控制发布(间接地,通过“喊叫”)。 此代码更简洁,可读且可重用。

我意识到这个问题现在已经很老了,但希望它可以帮助别人!

也许为每个长方法做一个SwingWorker。 每个SwingWorker都有自己的进度级别。

每个SwingWorker在doInBackground方法期间更新它自己的进度级别,然后调用publish。 在流程方法内部,所以在EDT内部,每个SwingWorker都会读取它的进度级别,并更新模型和愿景的一般进度条。

我遇到了类似的问题。 这是我发现的,也许真正正确的答案缺乏,但让我们尝试一下:

  • 如果我们有很多迭代,我们可以在doInBackGround()方法中更新进度条。 JProgressBar是SwingWorker的构造函数参数,它扩展了SwingWorker(所以是的,我们使用自定义)
  • 如果我们没有迭代并且我们不能中断需要花费大量时间来完成的方法,我们可以将整个事情搞砸并像大多数人一样(因此我们的进度条没有线性过程,但它只是在部分后刷新它的值工作完成了)。 坏消息是,如果我们的方法是唯一的工作人员(在背景上发送电子邮件),进度条将突然变满 。 虽然不是很好,但让我们来看看第三个选项
  • 这可能是相当疯狂和性能放缓,这都是因为我们的应用程序必须是花哨的 。 那么让我们来看一下需要很长时间才能完成的方法的源代码。 我们覆盖它并粘贴完全相同的代码,但我们再添加一个参数 – 猜猜是什么,是的JProgressBar。 在方法内部,我们创建了Thread,它将运行直到某个布尔参数(指示方法的标志最终完成)设置为true。 线程将在一些合理的时间间隔内不断更新JProgressBar。 最大的问题是假设,合理的间隔是多少。 我们应该进行一些测试并估计间隔值。

在第三点中,我描述了如何从方法执行Thread,该方法完成了一些非迭代的任务(至少在我们的Java代码中没有)并且不能被中断。 线程更新作为方法参数给出的JProgressBar。 然而,这与纯方法调用相比更加明确