バックグラウンド タスクでスローされた例外を SwingWorker が処理するというユーザビリティの問題に苦労してきました。たとえば、この SO スレッドで説明されています。そのスレッドは問題の適切な説明を提供しますが、元の例外の回復については議論していません。
渡されたアプレットは、例外を上方に伝搬する必要があります。しかし、私はそれを捕まえることさえできませんでした。このブログ エントリの SimpleSwingWorker ラッパー クラスを使用して、特にこの問題に対処しようとしています。かなり小さなクラスですが、参考のために最後に再掲します。
呼び出しコードは大まかに次のようになります
try {
// lots of code here to prepare data, finishing with
SpecialDataHelper helper = new SpecialDataHelper(...stuff...);
helper.execute(); // this will call get+done on the actual worker
} catch (Throwable e) {
// used "Throwable" here in desperation to try and get
// anything at all to match, including unchecked exceptions
//
// no luck, this code is never ever used :-(
}
ラッパー:
class SpecialDataHelper extends SimpleSwingWorker {
public SpecialDataHelper (SpecialData sd) {
this.stuff = etc etc etc;
}
public Void doInBackground() throws Exception {
OurCodeThatThrowsACheckedException(this.stuff);
return null;
}
protected void done() {
// called only when successful
// never reached if there's an error
}
}
の特徴SimpleSwingWorker
は、実際の SwingWorker のdone()/get()
メソッドが自動的に呼び出されることです。これにより、理論的には、バックグラウンドで発生したすべての例外が再スローされます。実際には、何も引っかからず、その理由もわかりません。
参照用の SimpleSwingWorker クラス。簡潔にするために何も省略していません。
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
/**
* A drop-in replacement for SwingWorker<Void,Void> but will not silently
* swallow exceptions during background execution.
*
* Taken from http://jonathangiles.net/blog/?p=341 with thanks.
*/
public abstract class SimpleSwingWorker {
private final SwingWorker<Void,Void> worker =
new SwingWorker<Void,Void>() {
@Override
protected Void doInBackground() throws Exception {
SimpleSwingWorker.this.doInBackground();
return null;
}
@Override
protected void done() {
// Exceptions are lost unless get() is called on the
// originating thread. We do so here.
try {
get();
} catch (final InterruptedException ex) {
throw new RuntimeException(ex);
} catch (final ExecutionException ex) {
throw new RuntimeException(ex.getCause());
}
SimpleSwingWorker.this.done();
}
};
public SimpleSwingWorker() {}
protected abstract Void doInBackground() throws Exception;
protected abstract void done();
public void execute() {
worker.execute();
}
}