2010-06-14 92 views
2

我有这样的代码:Swing invokeLater永不出现,invokeAndWait抛出错误。我能做什么?

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

我读的InputStream,计算一个进度条的值,并将其设置到。所以,当我点击一个按钮时,一个新的JFrame打开并执行上面所写的所有内容。

如果我自己有dialog.handleDownload(没有SwingUtilities方法),它会冻结,直到操作完成。如果我将它添加到invokeLater中,它会非常快地关闭(我看不到任何东西,操作未完成)。如果我将它添加到invokeAndWait中,则得到invokeAndWait不能从事件分派器线程错误中调用。我该怎么办?

回答

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); 
} 

编辑:更改了示例以驱动进度条,就像发生下载一样。

+0

这是一个非常好的例子。我会改变这个循环:'for(int i = 0; i <100; i ++){ \t num + = 5; \t publish(num); \t Thread.sleep(100); \t}' – jjnguy 2010-06-14 18:41:42

+0

@jjnguy:在这个例子中,我实际上想要证明发布次数并不决定多少次调用过程(即,中间结果会自动为你整理)。增加睡眠会将其改变为1:1的比例。是否有特定的理由表明我错过了? – 2010-06-14 19:01:11

+0

@Mark,在我的机器上,这个号码刚刚起飞,对我来说似乎没有任何意义。让它移动得慢一点帮助我看看实际发生了什么。 – jjnguy 2010-06-14 19:24:38

4

如果您是通过点击按钮来做到这一点的,那么您已经在事件线程中,因此invokeAndWait实际上会沿着错误的方向行进。

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

当你在新的线程中运行,确保任何GUI更新使用invokeAndWait或最好的invokeLater来回到美国东部时间。

简单的规则要记住:

  • 被Swing交给你任何线程是EDT,所以做这一切的GUI的东西,你想
  • 请在EDT GUI元素的所有更新(只要)。
  • 在非EDT线程上执行任何需要很长时间的操作(启动一个新线程)。
  • 使用了invokeLater从非EDT线程
1

你不应该访问的事件线程您的inputStream回到了EDT。产生一个新的线程,它实际上处理了handleDownload()的大部分工作,然后让该线程执行的最后一个操作是用实际显示并填充对话框的代码调用SwingUtilities.invokeLater()。

1

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

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) { 
     } 
    } 
}