SwingWorkerは、サン・マイクロシステムズJava言語のSwingライブラリ向けに開発した、一般的なユーティリティークラスであり、イベントディスパッチスレッドを適切に利用できるようにするものである。Java 6になって、JREに含まれるようになった。 SwingWorkerは、いくつかの非互換・非公式なバージョンが、1998年から2006年にかけて開発されてきた経緯があるため、これらのJava 6以前のバージョンに対する説明書を使わないよう注意を払うべきである。

Java 6.0での使用

編集

イベントディスパッチスレッドの問題

編集

SwingWorkerが有効なのは、ユーザーの操作イベントに引き続いて、時間を要するタスクが実行されねばならない場合である(たとえば、JButtonを押下した後に巨大なXMLファイルをパース(構文解析)するなどの場合)。これを単純にコーディングすると、次のようになるが、

private Document doc;
...
JButton button = new JButton("Open XML");
button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            doc = loadXML();
        }
    });

これは動作はするものの、loadXML()メソッドがメインのSwingスレッドと同じスレッド(イベントディスパッチスレッド)の中で呼び出されることになるため、このメソッドの処理に時間がかかるなら、その間GUIフリーズしてしまうことになる。

SwingWorkerを用いた解決法

編集

この問題はJava固有のものではなく、多くのGUIモデルに共通して起こる問題である。SwingWorkerはこれを解決する手段として、時間を要するタスクを別のバックグラウンドスレッドで処理するようにしている。これにより、この間のGUIの反応は維持されるようになる。

ワーカーの作成

編集

次のコードは、loadXML()メソッドの呼び出しを包み込んだSwingWorkerを定義している。

SwingWorker worker = new SwingWorker<Document, Void>() {
    public Document doInBackground() {
        Document intDoc = loadXML();
        return intDoc;
    }
};

ワーカーの実行

編集

ワーカーの実行は、SwingWorker.execute()メソッドを使って開始できる。

結果の取得

編集

結果は、SwingWorker.get()メソッドを使って取得できる。 しかし、イベントディスパッチスレッド上でget()を呼び出すと、件のタスクが完了するまでの間、再描画を含むすべてのイベントをブロックしてしまうため、長時間にわたるオペレーションの場合それが完了する前にget()を呼び出すのは避けるべきである。タスク完了の後で結果を取得する方法は2つある。

  • SwingWorker.done()メソッドをオーバーライドする。このメソッドは、メインのイベントディスパッチスレッドの中で呼び出される。
private Document doc;
...
SwingWorker worker = new SwingWorker<Document, Void>() {
    public Document doInBackground() {
        Document intDoc = loadXML();
        return intDoc;
    }
    public void done() {
        doc = get();
    }
};
  • SwingWorker.addPropertyChangeListener(PropertyChangeListener)メソッドを使ってリスナーを登録する。このリスナーには、ワーカーの状態の変化が通知される。

完全なワーカーの例

編集
private Document doc;
...
JButton button = new JButton("Open XML");
button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            SwingWorker<Document, Void> worker =
                new SwingWorker<Document, Void>() {
                public Document doInBackground() {
                    Document intDoc = loadXML();
                    return intDoc;
                }
                public void done() {
                    doc = get();
                }
            };
            worker.execute();
        }
    });

歴史: Java 6.0以前の利用

編集

SwingWorkerはJava 6.0になってはじめてJava SEの一部となった。Sunはそれ以前のJDKにおいて使われるべきバージョンをリリースしてきたが、それらはJava SEの一部ではない非公式なバージョンであって、標準ライブラリのドキュメントでは言及されていなかった[1]。これらの古いバージョンの中で最も新しいものは2003年以降のもので、しばしば SwingWorker version 3と呼ばれる[2]。残念なことに、JDK 6.0 の SwingWorker と Version 3 の SwingWorker は異なるメソッド名を使っているため、非互換である。Java 6以前ではバックポート版(後述)の利用が推奨される。

SwingWorker 3 をインスタンス化する例を以下に示す:

 SwingWorker worker = new SwingWorker() {
   public Object construct() {
     ... //バックグラウンドスレッドのコードを追加する
   }
   public void finished() {
     ... //ここに書いたコードはUI側のスレッドで走る
   }
 
 };
 worker.start();  //バックグラウンドスレッドを開始する

start()メソッドがconstruct()メソッドに追加されたコードを別スレッドで実行する。バックグラウンドスレッドが終わった時に通知を受けるには、finished()をオーバーライドするだけで良い。construct()メソッドは戻り値を返すことができ、この戻り値は後で SwingWorkerの get()メソッドを使って取得できる。

Java 6 SwingWorkerのバックポート版

編集

Java 6 SwingWorkerのJava 5 へのバックポート版がhttp://swingworker.java.net/で入手可能である。これは、パッケージ名 ( org.jdesktop.swingworker )を除けば、Java 6のSwingWorkerと互換性がある。

同等品

編集

参考

編集
  1. ^ J2SE 6.0 以前以後javax.swing パッケージ概要をみよ。version 6.0までは、このドキュメントの目次ページに SwingWorker クラスが挙げられていないことにも注目されたい : [1], [2]
  2. ^ ここからダウンロードできる。