タイマー処理=一定周期で処理を行う、てのをkotlinで極力カンタンに書いてみる(Handler利用)
どうも!LSSです!
今回は、タイマー処理について。
- Timerという、そのものズバリな選択肢…は使わない!
- じゃあ、どうすんのさ?!
- あれ?Timer要らなくない??
- 新規アプリ作成
- kotlinコードを書いていきます!まずは変数とHandlerの宣言から!
- タイマー処理したい内容を設定します
- 仕上げ
一応、先に完成したコードを載せておきます。
package jp.littlestrangesoftware.a20191005handler |
一定周期で処理を行いたい場合。
例:カップラーメン作るのにタイマーを作って、残り時間をリアルタイムに表示するアプリなど
事前に色々調べてみたのですが、Androidアプリ開発においてタイマー処理を行うのにも、何通りかの方法があるようで。
Timerという、そのものズバリな選択肢…は使わない!
で、まっさきに名前が挙がるのが、名前もそのまんま
Timer()
ってのがあります。
…が!
「Timerによって呼び出された処理は別スレッドで実行されるため、そこに画面描画に関する処理を書けない」
という問題があります。
※画面描画に関する処理はメインスレッドのみが実行可能。
別スレッドで実行された方が都合のいい場合もあるのかもですが、今のところ、
「単にタイマー処理したいだけ」
なので、ちょっと敷居が高くなるだけかな?って思ってしまいます。
(理解がすすめば、なぜそんな仕様になっているのか納得できる理由が分かるのかもですが、今んところまだ^^;。)
じゃあ、どうすんのさ?!
じゃあTimerで一定周期処理を行うのに、そのタイミングで画面の内容を更新したかったらどうするの?っていうのには答えがあって、
「Timerで呼び出された処理から、Handlerを使って、メインスレッドに『これやっといて』って画面描画に関する処理を投げる」
ことで、対応が可能。
つまり、
- Timerを用意する
- Handlerを用意する
- Handlerでメインスレッドに依頼する処理を用意する
という工程を踏む事で、一定時間ごとに処理を行うプログラムが書けるのですね。
それはそれで解決!なのですが、このHandlerというのが、これ自体、
「〇ミリ秒後に指定の処理を依頼」
という設定を持っています。
あれ?Timer要らなくない??
すると、
「Handlerで呼び出す処理の中に、再度『〇ミリ秒後に処理を呼び出す』って書いたら、それだけで完結するんじゃないか?」って発想が出てきます。
それだとTimerを用意しなくていいし、画面更新の可否を気にしなくてもいいし。
というわけで、今回は「極力カンタンに」という事でHandler頼みのタイマー処理サンプルを作ってみます!
新規アプリ作成
EmptyActibityを選択して、Nextで進みます。
次の画面では、Nameに適当なアプリ名だけ入れて、Finishをクリックします。
少し待つと、おなじみの編集画面が出てきます。
今回はまず、activity_main.xmlをクリックします。
ここでは、左下のTextViewをクリックして選択し、右上のidをクリックします。
TextViewに名前を付けるのですが、とりあえず「t0」とでもしておきます。
xmlの編集は以上で終わり!(TextViewにidを設定しただけ)
コードの編集の方に移ります。
MainActivity.ktをクリックします。
また、importの左の+マークをクリックして、importの内容を開いておきます。
kotlinコードを書いていきます!まずは変数とHandlerの宣言から!
ここからコードを書いていきます。
class MainActivity : AppCompatActivity() {
と
override fun onCreate(savedInstanceState: Bundle?) {
の間にEnterキー連打でスキマ行をあけておきます。
そのスキマにコードを書いていきますね。
まず、サンプルの動作を確かめるために使う変数、
var cnt=0
を入力します。そして改行して、
val hnd0=Hand
あたりまで打ったところで、
↑こんな候補が出てきます。
Handler、複数ありますが、今回は(android.os)とついているほうを選択します。
選択した後、その末尾に()を追加で付け足しておきます。
タイマー処理したい内容を設定します
次に、処理内容を設定します。今回の要ですね。
val rnb0=object : Run
あたりまで打つと、
また↑こんな候補が出てきます。
ここでは、Runnable(java.util)を選択します。
Runnable{}という補完結果になるので、{と}の間でEnterキーを数回押して、スキマ行を用意しておきます。
↑のようになります。
objectに赤い波線がついていますが、とりあえず気にしない事にします。
続けて、{と}のスキマ行に書き込んでいきます。
over
とだけ打ったところで、
↑こんな候補が出てきます。
ここでは
override fun run(){...}
を選択しておきます。
補完され、↑のようになります。TODO行が選択された状態で、キーボードのDelキーを押して消しておきましょう。
さて、この
run(){ }の{と}の間に書く内容が、いよいよ一定周期で行いたい処理内容そのものになります!
まずは、
cnt++
t0.text=cnt.toString()
と入力します。
↑こんな警告が出てきます。
xml上にあるt0を扱う準備ができていなかったからですね。
キーボードのALTキーを押しながらEnterキーを押すと解決します。
続けて、
if(cnt<50){
hnd0.postDelayed(this,100)
}
と入力します。
「cntが50より少なかったら、ハンドラーhnd0君は100ミリ秒後(=0.1秒)にthis(この処理。つまりこれが書かれているrunの内容そのもの)が再実行されるようにしておいてね。」
って事ですね。
呼び出されるたびに、先に書いたcnt++によってcntの値は1づつ増えていき、その時点でのcntの値はTextViewに反映されます。
cntが50未満なら0.1秒後に再度実行、cntが50になるまでは繰り返されます。
(50になったら次の呼び出しが行われないので、ループ終了です。)
仕上げ
全ての用意は整いました!
あとはこのループの第一回目を開始させる処理を書きます。
onCreate内に
hnd0.post(rnb0)
と書くだけです。
あと、余分な改行を削除したりした結果が↓こちら!
エミュレータでビルドしてみます。
↑静止画では分かりませんが、画面中央に数字が結構な勢いで増えていき、50になったら止まる、というサンプルの完成です!
今回のコードで、タイマー処理として必要な部分を、赤太字で示すと、
package jp.littlestrangesoftware.a20191005handler |
↑こういう感じですね。
Runnableのあたりが少し面倒ですが、結構扱いやすくまとめられたかな、と思ってますw
※強いて言えば、今回の例は50まで処理したら終わり、なのでいいけど、定められた終わりがなく「ユーザーがボタンを押したら終わり」とかにしたい場合に必要なキャンセル処理に触れてないのが心残りです^^;
タイマー処理のひな型が出来たので、できる事の幅がぐんと増えました!
ってなところで、また次回!よろしくお願いいたします。