Swing invokeLater从不显示,invokeAndWait抛出错误。我能做什么?

时间:2010-06-14 18:05:21

标签: java swing

我有这段代码:

try {
    SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
        try {
            dialog.handleDownload();
        } catch (IOException io) {
            io.printStackTrace();
            }
        }
    });
} catch(Exception io) { io.printStackTrace(); }

handleDownload我正在读取输入流,计算进度条的值,并将其设置为该值。所以,当我点击一个按钮时,会打开一个新的JFrame并完成我上面写的所有内容。

如果我自己拥有dialog.handleDownload(没有SwingUtilities方法),它会冻结直到操作完成。如果我将它添加到invokeLater中,它会非常快地关闭(我看不到任何内容,操作也没有完成)。如果我在invokeAndWait中添加它,则无法从事件调度程序线程错误中调用invokeAndWait。我该怎么办?

5 个答案:

答案 0 :(得分:4)

如果你这样做是为了响应按钮点击,那么你已经在事件线程中,所以invokeAndWait实际上是在错误的方向。

您需要启动一个新线程来执行不是事件派发线程的handleDownload线程 - 但

在新线程中运行时,请确保所有GUI更新都使用invokeAndWait或最好使用invokeLater返回EDT。

要记住的简单规则:

  • Swing交给你的任何帖子都是EDT,你想要的所有GUI内容也是如此
  • 在EDT(仅限)上进行GUI元素的所有更新。
  • 在非EDT线程上做任何需要很长时间的事情(开始一个新线程)。
  • 使用invokeLater从非EDT线程返回EDT

答案 1 :(得分:4)

看起来您可以使用SwingWorker。这允许您将昂贵的操作推迟到后台线程(保持GUI响应),并在操作完成后,对GUI执行一些操作。

编辑:示例

这是一个更复杂的示例,展示了如何使用SwingWorker的基础知识,以及如何发布/处理中间结果。

public static void main(String[] args) {
    final int SIZE = 1024*1024; //1 MiB

    //simulates downloading a 1 MiB file
    final InputStream in = new InputStream() {
        int read = 0;
        public int read() throws IOException {
            if ( read == SIZE ) {
                return -1;
            } else {
                if ( read % 200 == 0 ) {
                    try { Thread.sleep(1); } catch ( InterruptedException e ) {}
                }
                read++;
                return 5;
            }
        }
    };

    final JProgressBar progress = new JProgressBar(0, SIZE);

    final JButton button = new JButton("Start");
    button.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            button.setText("Working...");
            SwingWorker<byte[], Integer> worker = new SwingWorker<byte[], Integer>() {
                @Override
                protected byte[] doInBackground() throws Exception {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    for ( int read = -1; (read = in.read(buff)) != -1; ) {
                        baos.write(buff, 0, read);
                        publish(read);
                    }
                    return baos.toByteArray();
                }

                @Override
                protected void process(List<Integer> chunks) {
                    int total = 0;
                    for ( Integer amtRead : chunks ) {
                        total += amtRead;
                    }
                    progress.setValue(progress.getValue() + total);
                }

                @Override
                protected void done() {
                    try {
                        byte[] data = get();
                        button.setText("Read " + data.length + " bytes");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            worker.execute();
        }
    });

    JFrame frame = new JFrame();
    frame.setLayout(new BorderLayout());
    frame.add(button, BorderLayout.NORTH);
    frame.add(progress, BorderLayout.SOUTH);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack(); frame.setVisible(true);
}

编辑:更改示例以驱动进度条,就像正在进行下载一样。

答案 2 :(得分:1)

您不应该在事件线程中访问您的inputStream。生成一个实际执行handleDownload()大部分操作的新线程,然后使该线程执行的最后一个操作是使用实际显示和填充对话框的代码调用SwingUtilities.invokeLater()。

答案 3 :(得分:1)

“handleDownload”有什么作用?不应该在事件调度程序线程中完成耗时的事情。如果事件调度程序线程中出现大量CPU周期,则显示将冻结,直到完成为止。在调用普通线程(不使用SwingUtilities)在事件调度程序线程之外进行处理,并在该线程中使用SwingUtilities.invokeLater发送事件已更改的通知(如更新进度条)定期。

答案 4 :(得分:1)

您需要的是SwingWorker。这将允许您在不打扰EDT的单独线程中进行文件下载。

您的代码看起来像这样:

class Downloader extends SwingWorker<String, Void> {
   @Override
   public String doInBackground() {
       dialog.handleDownload();
       return "done";
   }

   @Override
   protected void done() {
       try { 
           someLabel.setText(get());
       } catch (Exception ignore) {
       }
   }
}