UIスレッドでのタイマー実行
ユーザインタフェースを操作する処理を一定時間ごとに繰り返し行いたい場合,スレッドを使うのが一般的である。しかし,普通に自前で生成したスレッドからはSWTのUIコンポーネントを操作することができないために,DisplayクラスのsyncExec()やasyncExec()を使用する必要がある。ある間隔を持って処理を繰り返し行う場合は,Thread.sleep(500)というようにしてスレッドの実行を停止させることが思いつく方法だが,SWTではOSのタイマーイベントを使用する方法が提供されている。
OSが持つタイマーイベントを利用するには,DisplayクラスのtimerExecメソッドを使用する。
Runnable runnable = new Runnable() {
public void run() {
// 繰り返し行いたい処理
Display display = ...;
if (!display.isDispose())
display.timerExec(500, this);
}
};
Display display = ..;
if (!display.isDispose())
display.timerExec(500, runnable);
繰り返し行いたい処理は,Runnableインタフェースの実装として作成する。上記では,匿名クラスとしてRunnableインタフェースの実装オブジェクトを生成している。そして,Displayオブジェクトが破棄されているかどうかをチェックし,破棄されていなければ,DisplayオブジェクトのtimerExecメソッドに時間とRunnableオブジェクトを渡す。第1引数は,Runnableオブジェクトのrunメソッドを呼び出すまでの待ち時間を指定する。上記では,0.5秒後にrunメソッドを呼び出すように指定している。
timerExecメソッドによって,OSにタイマーの登録が行われ,指定時間後にタイマーイベントが発生し,それを契機としてrunメソッドが実行される。ただし,実行されるのは1回だけ。繰り返し処理を行いたい場合は,runメソッド内で再度timerExecメソッドを呼び出して再びrunメソッドが実行されるように登録すればよい。その際に,Displayメソッドが破棄されているかどうかをチェックする必要がある。
このrunメソッドの呼び出しはUIスレッドが行うので,SWTのUIをrunメソッド内で操作することが可能だ。
あるUIコンポーネントを定期的に操作する方法としては,原則として上記のtimerExecメソッドを使用するべきだ。例えば,下記のように自前でスレッドを生成し,その中で繰り返しsyncExecメソッドなどの呼び出しを行ってUIコンポーネントを操作した場合は,他のコンポーネントに影響が発生してしまう。
Runnable runnable = new Runnable() {
public void run() {
while(true) {
Display display = ..;
if (!display.isDispose()) {
display.syncExec(new Runnable() {
public void run() {
// 繰り返し処理
}
});
}
Thread.sleep(500);
}
}
};
(new Thread(runnable)).start();
上記のようなコードでも,繰り返し処理はちゃんと実行されるだろう。しかし,DirectoryDialogクラスを利用して実現されるディレクトリ選択ダイアログの動作が,正しく行われなくなってしまう。具体的には,サブディレクトリやアイコンの読み込みがマルチスレッド化されているのだが,そのスレッドが動作しなくなってしまい,ディレクトリの選択ができないといった現象になる。
気軽にスレッドを作ることはできるが,その結果思わぬ不具合を引き起こしてしまう可能性があるので,気をつけて欲しい。
| 固定リンク | コメント (16) | トラックバック (1)