TITLE:SwingWorkerを使った処理の中断と進捗状況表示

Posted by aterai at 2006-06-10

SwingWorkerを使った処理の中断と進捗状況表示

JDK 6 で新しくなったSwingWorkerを使って、処理の中断や進捗状況の表示更新などを行います。

  • &jnlp;
  • &jar;
  • &zip;
SwingWorker.png

サンプルコード

class RunAction extends AbstractAction{
  public RunAction() {
    super("run");
  }
  @Override public void actionPerformed(ActionEvent evt) {
    //assert EventQueue.isDispatchThread();
    System.out.println("actionPerformed() is EDT?: "
        + EventQueue.isDispatchThread());
    final JProgressBar bar = new JProgressBar(0, 100);
    runButton.setEnabled(false);
    canButton.setEnabled(true);
    icon.animationStart();
    statusPanel.removeAll();
    statusPanel.add(bar);
    statusPanel.revalidate();
    bar.setIndeterminate(true);
    worker = new SwingWorker<String, String>() {
      @Override public String doInBackground() {
        System.out.println("doInBackground() is EDT?: "
            + EventQueue.isDispatchThread());
        try { // dummy task
          Thread.sleep(1000);
        }catch(InterruptedException ie) {
          return "Interrupted";
        }
        int current = 0;
        int lengthOfTask = 120; //list.size();
        publish("Length Of Task: " + lengthOfTask);
        publish("------------------------------");
        while(current<lengthOfTask && !isCancelled()) {
          try { // dummy task
            Thread.sleep(50);
          }catch(InterruptedException ie) {
            return "Interrupted";
          }
          setProgress(100 * current / lengthOfTask);
          //worker.firePropertyChange("progress", current, current+1);
          current++;
        }
        return "Done";
      }
      @Override protected void process(java.util.List<String> chunks) {
        //assert EventQueue.isDispatchThread();
        System.out.println("process() is EDT?: "
            + EventQueue.isDispatchThread());
        for(String message : chunks) {
          appendLine(message);
        }
      }
      @Override public void done() {
        //assert EventQueue.isDispatchThread();
        System.out.println("done() is EDT?: "
           + EventQueue.isDispatchThread());
        icon.animationStop();
        runButton.setEnabled(true);
        canButton.setEnabled(false);
        statusPanel.remove(bar);
        statusPanel.revalidate();
        String text = null;
        if(isCancelled()) {
          text = "Cancelled";
        }else{
          try {
            text = get();
          }catch(Exception ex) {
            ex.printStackTrace();
            text = "Exception";
          }
        }
        appendLine(text);
      }
    };
    worker.addPropertyChangeListener(new ProgressListener(bar));
    worker.execute();
  }
}

class CancelAction extends AbstractAction{
  public CancelAction() {
    super("cancel");
  }
  @Override public void actionPerformed(ActionEvent evt) {
    if(worker!=null && !worker.isDone()) {
      worker.cancel(true);
    }
    worker = null;
  }
}

解説

以前のSwingWorker.javaから一部メソッド名が変更されていますが、基本的な使い方は一緒のようです。

  • SwingWorker#execute()メソッドで処理が開始され、SwingWorker#doInBackground()メソッドが、バックグラウンドのスレッドで実行されます。
  • EDTで実行する必要のある処理(上記の例では処理中にJTextAreaへのメッセージの書き出し)は、SwingWorker#process()メソッドをオーバーライドしてSwingWorker#publish()メソッドで呼び出したり、SwingWorker#firePropertyChange()を使えば良いようです。
  • プログレスバーの処理には、SwingWorker#setProgress(int)が予め用意されているので、SwingWorker#addPropertyChangeListener(ProgressListener)を設定するだけで使用することが出来ます。
  • 実行中の処理のキャンセルは、SwingWorker#cancel(boolean)メソッドで行います。キャンセルされたかどうかは、SwingWorker#isCancelled()メソッドで知ることが出来ます。

EventQueue.isDispatchThread()を使うと以下のようになっています。

  1. actionPerformed() is EDT?: true
  2. doInBackground() is EDT?: false
    • ここ(バックグラウンド)で重い処理を行い、EDTを停止(ブロック)しないようにする
  3. process() is EDT?: true
    • コンポーネントで進捗状況を表示する場合は、EDTで行う必要があるので、ここ(process()メソッド内)で実行する
  4. done() is EDT?: true

参考リンク

コメント