UIスレッドの処理をTimerを使わずHandlerだけで簡単定期実行したい
『1秒間隔で定期的に何かの処理を行う』という実装を行う際、パッと思いつくのはTimerとTimerTaskを使った以下のような実装。
ただ、その際TimerTask.run()の中で実行されるコードはTimerのThreadなので、
UIパーツへの操作など、UIスレッドで実行が必要な操作をするにはHandlerを使う必要があります。
課題
そうなると、
- UIスレッドがTimerを実行
- TimerスレッドがUIスレッドに返す
- UIスレッドでdoSomething実行
みたいな、ちょっとわかりづらい処理になってしまいます。
コードだとこんな感じが必要になります。
(ネストが深いですが、処理のループがわかりやすいので、そのまま。)
Before
final Handler handler = new Handler();
TimerTask task = new TimerTask() {
int count = 0;
@Override
public void run() {
// Timerのスレッド
handler.post(new Runnable() {
@Override
public void run() {
// UIスレッド
if (count > 5) { // 5回実行したら終了
cancel();
}
doSomething(); // なにかやる
count++;
}
});
}
};
Timer t = new Timer();
t.scheduleAtFixedRate(task, 0, 1000);
対策
TimerとTimerTaskがほとんど何もやってない感じなので、その2クラスを消したいです。
HandlerのpostDelayedを使うと、Timer・TimerTaskを削除して、定期実行することができるようになります。
After
final Handler handler = new Handler();
final Runnable r = new Runnable() {
int count = 0;
@Override
public void run() {
// UIスレッド
count++;
if (count > 5) { // 5回実行したら終了
return;
}
doSomething(); // 何かやる
handler.postDelayed(this, 1000);
}
};
handler.post(r);
キャンセルしたいときは
handler.removeCallbacks(r);
で、runnableとの関係を切ってあげればOKです。
割と直感的なコードになりました。
この程度であればお手軽にUIスレッドでも定期実行できそうです。