2010-06-14 86 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) { 
     } 
    } 
}