[自分用メモ] AndroidでのTimerとThreadとHandlerの連携について今一度自分なりの整理をしてみた

Androidプログラミングを学習し始めてまだまだ間もないが(別の仕事をしながらなので、なかなか時間がとれない)、サンプルコードを入力する修行は先月ぐらいに脱して今は当面の目的に向かってコードを書いている。
とはいえ、サンプルコードを淡々と打ちながら学習しているだけではやはり理解度に問題が出てくるので、自分なりに整理してコードをスクラッチから書かないと身にならない。これは昔から同じで、ベーマガでBASICを勉強していたウン十年前から学習手法・技法としては変わっていないし、結構普遍的なことだとは思っている。

Androidの開発に慣れていないからなのだが、どうにも理解しにくい面が多い。スマートフォンアプリケーションの開発というのはAndroidとiOSの2大巨頭しかないので、スマフォ開発特有の概念というものは過去の開発から想像できないことが多い。特にSDKには悩まされることが多い。名前から想像できないものや、UIがらみで多い他のクラスとの併用パターンなどは、もうサンプルから読み取り覚えるしか方法はない。SDKドキュメント(英語)を読めば書かれているのかもしれないけれど、そもそも実現するためにどのSDKを使えばいいのかという手掛かりに欠けるため、ドキュメントを読むのはどうしても後回しになりがちになってしまう。

個人的に現時点でウヤムヤとしているのはUIまわりだとListViewとArrayAdapterとの関係性だが、これも結局は理屈抜きに「こういうもの」として覚えるしかないだろう。実際にそういう使い方が想定されているのだと思う。

もう一つはThread、Timerを併用で、混乱していたから自分でもはっきり覚えていないが、たぶんThread内でThreadを呼び出していたか、UI周りをメインスレッド以外で処理しようとした(Handlerを使わなかった)かというあたりの問題だったかもしれない。

いずれにしてもコードを書き換えて実験していると、あいまいなまま覚えたり間違って学習してしまう危険性が高いので、独立した単独での実証コードを書いて確認するというフェーズは欠かせない。ということで、ざっくり書いてみた。クラス名TttActivityは単にThreadTimerTestの略なので気にしないように。

[java]
package com.example.timerthreadtest;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.TextView;

public class TttActivity extends Activity {

private int count = 0;
private TextView tv;
private Handler handler = new Handler();
private Timer mytimer = new Timer();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);

LinearLayout ll = new LinearLayout(this);
ll.setBackgroundColor(Color.WHITE);
ll.setOrientation(LinearLayout.VERTICAL);
setContentView(ll);

tv = new TextView(this);
tv.setBackgroundColor(Color.rgb(224, 224, 224));
tv.setTextColor(Color.GREEN);
ll.addView(tv);
}

public void onResume() {
super.onResume();
mytimer.schedule(new MyTimer(), 0, 1000);
}

public void onPause() {
super.onPause();
mytimer.cancel();
}

private class MyThread extends Thread {
public void run() {
handler.post(new Runnable(){
public void run() {
tv.setText("count=" + count++);
}
});
}
}

private class MyTimer extends TimerTask {
public void run() {
MyThread mythread = new MyThread();
mythread.start();
}
}
}
[/java]

まずこのアプリが何をするかというと、単に1秒ごとにカウンター変数countを+1させて、TextViewを通じて「count=123」のようにカウント値を表示させ続けるというだけのものである。特別なことは何もしていないが、ここに出てくる要素は以下のとおり。()内は関連クラス。

・タイマーで定期処理を行う(Timerクラス、TimerTaskクラス)
・スレッド内でUIを操作する(Threadクラス、Handlerクラス、Runnableクラス)

16行目はカウンター用のメンバ変数の宣言で0で初期化。
17行目はTextViewインスタンスの生成。インナークラスであるMyThreadでハンドラに渡すのでメンバ変数として宣言する。初設定はonCreateメソッドで行うので、ここでは宣言のみ。
18行目はハンドラインスタンスの生成。Androidの設計思想上(と思われる)、UIの操作はメインスレッドでのみ許可されているため、ここでインスタンスを生成してクラス内で使用する。
19行目はタイマインスタンスの生成。

21~35行目はごく普通のonCreate処理で、LinearLayoutをContentViewにしたあと、この上にTextViewを1つだけ割り当てている。

37~40行目のonResumeメソッドは、Activityが前になった時に呼び出される処理で、この時点でカウント処理を開始するようにスケジュールを設定している。
42~45行目のonPauseメソッドは逆にActivityが背面に隠れた場合に呼び出される処理で、スケジュールをキャンセルしている。
つまり、Activityの前後によってスケジュールを設定/解除している。隠れている場合にはカウント処理は進まない。
なお39行目のscheduleメソッドの引数は順に「TimerTaskを継承したコールバックインスタンス」「呼び出し遅延時間(マイクロ秒)」「呼び出し周期(マイクロ秒)」。

[Sponsored Link]


47~55行目はインナークラスでThreadを継承したMyThreadクラスを宣言している。これは、この後で宣言しているインナークラスのMyTimerでスケジュールが実行されるたびにインスタンスを生成してスレッドを実行するようにしている。
48~54行目のrunメソッドにスレッドとして実行する内容を記述するが、ここでカウンタ値をTextViewに表示する処理を行いたい。しかしメインスレッドではないのでUI操作は不可能。そのためhandler.postメソッドにRunnableの無名インスタンスを登録して、このrunメソッドを実行させるように設定する。
この手の処理では様々なrunメソッドが乱立しやすい感じがする。混乱しないように注意しなければならないだろう。
余談だが、51行目はcount++でも++countでも好きな方を使えばいいだろう。カウンタの開始番号はこのアプリでは一切こだわっていないのでcount++であることに特に意味はない。

57~62行目でTimerTaskクラスを継承したMyTimerクラスを宣言している。ここでもrunメソッドを実装しなければならず、これはscheduleメソッドで指定された呼び出し期間に基づきコールバックされるメソッドである。
59行目でスレッドのインスタンスを生成し、60行目でスレッドを開始させている。

あまり凝ったことをせず、シンプルなコードに一から書き直すことで見通しもよくなり理解の助けにもある。自分でフルスクラッチで書くことに意義があり、たとえ自分がサンプルコードを見ながら書いたものであったとしても、他のコードをコピペせずに、目で見たり頭で考えたものを1文字たりともキーボードで埋めるようにすることで、初めて自分のコードになるのだと思っている。

アプリを作ろう!  Android入門~ゼロから学ぶアプリの作成から公開まで本格アプリを作ろう! Androidプログラミングレシピ作ればわかる! Androidプログラミング 第2版 -SDK4対応- (Smart Mobile Developer)スマートにプログラミング Android入門編 第3版 SDK4.x対応Androidアプリ開発逆引き大全 500の極意―Android SDK/JDK対応 Eclipse対応小岩井 純水りんご 1.5L×8本

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください