手書きのタブレット向けアプリで、ペンの入力が連続して発生するのだが、そのたびに保存処理を行うと作動が重くなる。
そこで、ちょっと入力が止んだそのスキを見計らって、保存処理を実行するようにしたい。
そのための java.util.Timer と TimerTask の使い方のメモ。
例によってコードはすべて Groovy です。
// 0.5秒おきに 5件 のイベントを発生させる.
int cnt = 5
while( cnt>0 ){
cnt = cnt-1 // count down
println "- ${cnt}"
Thread.sleep(500)
}
def process = {
int cnt = 5
while( cnt>0 ){
cnt = cnt-1 // count down
println "- ${cnt}"
Thread.sleep(500)
}
}
// 0.5秒おきに 5件 のイベントを発生させる.
process()
// 1.5秒休む
Thread.sleep(1500)
// 0.5秒おきに 5件 のイベントを発生させる.
process()
問題は、小休止をどうやって判定するか。 いろいろな方法がありそうですが、ここでは、イベントが起きたら1秒後に実行するTimerTask を起動する方法を使います。 ロジックは以下の通り:
これにより 1秒より長い間隔の小休止が発生したら処理をする を実現してみます。
最初のステップとして、とにかくイベント起きたら、TimerTask を生成して 1秒後に処理を実行する、を実装:
import java.util.Timer
import java.util.TimerTask
class MyTimerTask extends TimerTask {
int myCnt
void run(){
println "-- run timerTask ${myCnt}"
}
}
def process = { timer->
int cnt = 5
while( cnt>0 ){
cnt = cnt-1 // count down
println "- create timerTask ${cnt}"
def timerTask = new MyTimerTask([myCnt: cnt])
timer.schedule(timerTask, 1000) // 1秒後に起動するようにスケジュール.
Thread.sleep(500)
}
}
// timer 作成
def timer = new Timer(false)
// 0.5秒おきに 5件 のイベントを発生させる.
process(timer)
// 1.5秒休む
Thread.sleep(1500)
// 0.5秒おきに 5件 のイベントを発生させる.
process(timer)
実行すると以下のようになります:
- create timerTask 4
- create timerTask 3
-- run timerTask 4
- create timerTask 2
-- run timerTask 3
- create timerTask 1
-- run timerTask 2
- create timerTask 0
-- run timerTask 1
-- run timerTask 0
- create timerTask 4
- create timerTask 3
-- run timerTask 4
- create timerTask 2
-- run timerTask 3
- create timerTask 1
-- run timerTask 2
- create timerTask 0
-- run timerTask 1
-- run timerTask 0
まだ 直前の TimerTask の cancel 処理を入れていないので、すべての timerTask が 1秒遅れで 実行されています。
直前の TimerTask インスタンスをキャンセルすることで、1秒以上の間隔があるときだけ TimerTask が実行されるようにします。
(process 部分だけの抜粋)
def prevTimerTask = null
def process = { timer->
int cnt = 5
while( cnt>0 ){
// do cancel
prevTimerTask?.cancel()
cnt = cnt-1 // count down
println "- create timerTask ${cnt}"
def timerTask = new MyTimerTask([myCnt: cnt])
timer.schedule(timerTask, 1000) // 1秒後に起動するようにスケジュール.
// keep it
prevTimerTask = timerTask
Thread.sleep(500)
}
}
実行すると以下のようになります:
- create timerTask 4
- create timerTask 3
- create timerTask 2
- create timerTask 1
- create timerTask 0
-- run timerTask 0
- create timerTask 4
- create timerTask 3
- create timerTask 2
- create timerTask 1
- create timerTask 0
-- run timerTask 0
これで一応意図通りの作動になりました。
ただ、これ処理が終わってもプログラムが終了しないのが気持ち悪い。 Timer インスタンスが残っている状態なので、タブレットアプリなどではメモリリークするのかもしれません。
new Timer(false)
により、デーモンスレッドにするか、ユーザースレッドにするかの指定を行う。ここでは、false を指定してユーザースレッドにしている。
この場合、プログラムが終了しなくなる。
そこで:
new Timer(true)
としてデーモンスレッドにすると、プログラムは終了する。
しかし、最後の TimerTask 実行前にプログラムが終了するので、最後の TimerTask の処理は実行されない。
これはまずい。
もし今想定しているような、手書きアプリでの入力保存に使う場合、最後の入力データが保存されないことになる。
どうすりゃいいの?
あと、Timerインスタンスの生成コストは低いのかな?1秒おきに Timer インスタンスを生成するのだから、もし重い処理だったら本末転倒になる。