3

我正在重构一些运行多阶段过程的代码。每个步骤都在嵌套的java.awt.EventQueue.invokeLAter.... 调用中。它看起来有点像这样:

   import java.awt.EventQueue;


public class NestedInvokeLater {

    /**
     * @param args
     */
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                changeTabPanel();
                copySomeFiles();
                enableNextButton1();
                upDateProgressBar(10);
                java.awt.EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        readInFiles();
                        doSomethingToFiles();
                        upDateProgressBar(15);
                        java.awt.EventQueue.invokeLater(new Runnable() {

                            @Override
                            public void run() {
                                doSomethingElse();
                                upDateProgressBar(100);

                            }
                        });
                    }
                });

            }

        });

    };
}

我在 Java 方面还不够新,所以我不明白嵌套这些调用以向 EDT 添加“作业”的意义,而且我也不是 100% 有信心摆弄这些调用。我想我了解invokeLater调用的作用以及每个步骤的作用。如果这种理解是错误的,请纠正我:

invokeLater用于将一些调用添加到要在 Event Dispatch 线程中完成的作业列表中。Java 然后处理每次调用何时/如何完成,确保 EDT 和 GUI 在“后台”执行作业时不会锁定。

嵌套这些调用告诉我,我们应该排队一组作业,其中一个是排队一些东西,这将排队一些作业......其中一个是排队一些东西。但是只有在前一个工作完成后,第一个内部调用才会排队。一切都是按顺序发生的(这符合我对整个过程的理解),但我不明白为什么要使用嵌套请求来排队作业。如果我从头开始编写,我会为每次调用创建函数并依次调用它们。

我认识到,作为 Java 的新手,我可能会遗漏一些使这种嵌套变得重要的巨大东西。但是没有这方面的文档,代码中也没有关于嵌套的评论。

我错过了什么?什么,如果这段代码有什么意义的话?

4

4 回答 4

4

做这么多嵌套调用是没有意义的。它是基于良好的意图,但实施得很糟糕。

如果您想正确执行此操作,请使用SwingWorker.

文档SwingWorker有一个简洁的示例,说明您应该如何在应用程序的后台执行多个任务(PrimeNumbersTask那里显示的类)。

编辑:这是一个示例,说明在您的情况下您应该使用 SwingWorker 做什么。

class SequentialInvoker extends SwingWorker<Void, Integer> {
    @Override
    public void doInBackground() {

        changeTabPanel();
        copySomeFiles();
        enableNextButton1();
        setProgress(10);

        readInFiles();
        doSomethingToFiles();
        setProgress(15);

        doSomethingElse();
        setProgress(100);
    }
}

要在进度条上实际显示进度,请查看从SwingWorker文档中复制的以下代码:

JTextArea textArea = new JTextArea();
JProgressBar progressBar = new JProgressBar(0, 100);
SequentialInvoker task = new SequentialInvoker();
task.addPropertyChangeListener(
    new PropertyChangeListener() {
        public  void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())) {
                progressBar.setValue((Integer)evt.getNewValue());
            }
        }
    }); 

使用此代码,您的进度条将显示进度SwingWorker

于 2013-03-19T16:27:00.993 回答
2

这样做的一个优点是其他排队的事情可以在两者之间运行。因此,在执行 changeTabPanel() 的部分和执行 readInFiles() 的部分之间,GUI 将响应用户单击按钮等...

实际的实现有点混乱,并说明了(恕我直言)为什么匿名函数不是一个好主意。您倾向于使这三个部分成为“真实”功能并按顺序调用它们是一个很好的选择。但是,为了保持相同的逻辑,您真正需要做的是让它们成为三个可运行对象,并让每个 invokeLater 成为后续的一个。

@Cyrille 是正确的,在 EDT 上执行这些主要任务是不好的做法。

于 2013-03-19T16:32:04.690 回答
1

这里使用了三个作业invokeLater。每个人都做一件代价高昂的事情,打电话updateProgressBar,然后将下一个工作添加到 EDT。

问题是,如果代码只是继续下一个代价高昂的事情,而不是调用 invokeLater 来做,EDT 将没有机会重新绘制进度条以显示它的新值。这可能就是为什么工作在三个invokelater电话中中断的原因。

现在,这不是我所说的好代码。这是一种非常糟糕的做法:不应该在 EDT 中进行长时间的处理,因为它会阻塞所有内容并使 GUI 无响应。这应该更改,以便该过程在单独的线程中完成,然后仅调用invokeLater以更新进度条。

编辑:更一般地回答标题中的问题:几乎没有合理的理由来嵌套调用invokeLater. 当您这样做时,您会说“将这项工作排队,以便它在同一个线程中完成,但稍后当您觉得它会很好时”。因此,它为 GUI 的其余部分提供了重新绘制自身的机会,就像这里一样。但只有在 EDT 中有一个长时间运行的进程时才有意义,您应该始终避免这种情况。

于 2013-03-19T16:27:55.400 回答
0

您发布的代码对我来说绝对没有意义 - 您可以按顺序编写所有内容,因为您没有运行可能在 EDT 上发布事件的并行线程。您需要第一个invokeLater(),因为您使用 Swing 组件。

但是正如您的代码所暗示的那样,您正在执行一些相对冗长的操作:读取文件,对它们执行某些操作,......您应该在新的工作线程中运行这些方法,而不是 EDT。而且,在这些工作线程的 run() 方法中,您需要调用 EventQueue.invokeLater() 来更新您的 GUI。

于 2013-03-19T16:31:11.703 回答