Timerを使う

Androidでタイマーを使った処理を行う場合、TimerとTimerTaskによって実装する方法がある。これ自体は難しいことはないのだが、TimerTaskのrunメソッドは別スレッドで実行されるため、直接UIを更新することはできない。下記をあわせて参照していただくと良いだろう。

マルチスレッドとUIの基本

タイマー処理を書く

さて、具体的なTimerの使い方であるが、まずはTimerのインスタンスを生成し、それに対してscheduleメソッドを呼ぶだけである。scheduleメソッドは何種類かの形式があるが今回はscheduleAtFixedRate(TimerTask task, long delay, long period)のものを使う。

最初の引数はTimerTaskであり、オーバライドしたrunメソッドが指定した日時に別スレッドで実行される。2番目の引数は最初のrunメソッドが実行されるまでの時間をミリ秒で指定する。3番目の引数はrunメソッドをどれくらいの周期で実行するかをミリ秒で指定する。他のscheduleメソッドを使えば指定した日時に1回だけ実行することもできる。

package biz.office_matsunaga.android;

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

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class TimerTestActivity extends Activity {
    TextView textView1;
    Timer timer1;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        textView1 = (TextView)findViewById(R.id.textView1);
        timer1 = new Timer();
        timer1.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                Log.d("run", "TimerTask Thread id = " + Thread.currentThread().getId());
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        Log.d("run", "runOnUiThread Thread id = " + Thread.currentThread().getId());
                        textView1.setText((new Date()).toLocaleString());
                    }
                });
            }
        }, 0, 1000);
    }
}

上記のコードは1秒周期でTextViewの時刻を更新する時計アプリケーションである。既述のとおり、TimerTaskのrunメソッドから直接TextViewを更新できないので、runOnUiThreadメソッドを介して更新している。

scheduleメソッドとscheduleAtFixedRateメソッドの相違

scheduleメソッドとscheduleAtFixedRateメソッドの違いは、runメソッド実行の間隔が相対的か絶対的かにある。前者のrunメソッドの開始時刻は前の開始時刻からの相対的なものであるのに対し、後者は最初のrunメソッドの開始時刻からの絶対的なものである。先ほどのコードを少し書き換えて試してみた。countはActivityのインスタンス変数である。

        timer1.schedule(new TimerTask() {
            @Override
            public void run() {
                
                // TODO Auto-generated method stub
                Log.d("run", "TimerTask Thread id = " + Thread.currentThread().getId());
                try {
                    // 10回に1回2500msの遅延
                    Thread.sleep(count % 10 == 0 ? 2500 : 0);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                count++;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        textView1.setText((new Date()).toLocaleString());
                    }
                });
            }
        }, 0, 1000);

periodを1000msとしてあるrunメソッドの処理が何らかの理由により2500msかかったとしよう。scheduleメソッドでスケジューリングした場合、2500msのrunメソッド終了後に開始予定時刻が到来しているので次のrunメソッドが1回だけ実行(下記デバッグログ3行目)され、そこからまた1000ms周期となる。

02-25 22:16:26.997: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:27.997: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:30.488: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:31.499: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:32.498: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:33.498: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:34.498: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:35.498: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:36.498: DEBUG/run(5258): TimerTask Thread id = 10
02-25 22:16:37.498: DEBUG/run(5258): TimerTask Thread id = 10

開始予定時刻は2500msの間に2回到来しているが、scheduleメソッドでスケジューリングした場合は蓄積されない。runメソッドの処理に5000msかかろうとも次に実行されるrunメソッドは1回のみである。

一方、scheduleAtFixedRateメソッドでスケジューリングした場合、同じ事象が発生すると2500msのrunメソッド終了後に開始予定時刻が到来しているrunメソッドが立て続けに2回実行(下記デバッグログ3,4行目)される。また、scheduleメソッドの場合と異なり、後続のrunメソッドの開始時刻は2500msのrunメソッドの影響を受けずに最初のrunメソッドからの1000ms周期を保っている。

02-25 22:13:52.441: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:53.441: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:55.938: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:55.938: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:56.442: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:57.441: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:58.438: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:13:59.441: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:14:00.441: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:14:01.441: DEBUG/run(5072): TimerTask Thread id = 10
02-25 22:14:02.441: DEBUG/run(5072): TimerTask Thread id = 10

これは例えば単位時間当たりに一定数処理を行う必要があるような処理(例えば時計)に適していると言える。

(2012/02/25)

新着情報
【iOS Objective-C, Swift Tips】画像の向きを指定して保存する(Swift)
【iOS Objective-C, Swift Tips】UIImagePickerControllerの表示を日本語にする(Swift)
【iOS Objective-C, Swift Tips】ウィンドウの階層構造を3D表示する(Swift)

Copyright(C) 2004-2013 モバイル開発系(K) All rights reserved.
[Home]