TimerTaskの止め方

Timerを使って定期的に何かを実行したいときはTimerTaskを使うことが多いです。

TimerTaskを使っていると実行した後に処理を中断できないという問題で悩むことが多いので正しい(?)使い方をメモしておきます。

例えば100ミリ秒ごとにカウントして時間を表示するようなコードを書きました。

public class MainActivity extends Activity
{
    Timer timer = new Timer();

    long timeInMills = 0;
    Button countStartButton;
    Button countStopButton;
    TextView countText;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        countStartButton = (Button) findViewById(R.id.countStartButton);
        countStopButton = (Button) findViewById(R.id.countStopButton);
        countText = (TextView) findViewById(R.id.countText);
        
        countStartButton.setOnClickListener()
        {
            public void onClick(View v)
            {
                timer.scheduleAtFixedRate(new TimerTask()
                {
                    public void run()
                    {
                        timeInMills += 100;
                        countText.setText(Long.toString(timeInMills) 
                                + "mill seconds");
                    }
                }, 0, 100);
            }
        }
        
        countStopButton.setOnClickListener()
        {
            public void onClick(View v)
            {
                timeInMills = 0;
                timer.cancel();
            }
        }
    }
}

上のはTimerTaskcancelしても永遠に動き続けてしまう失敗例です。

正しくは次のようにします。

public class MainActivity extends Activity
{
    Timer timer = new Timer();

    long timeInMills = 0;
    Button countStartButton;
    Button countStopButton;
    TextView countText;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        countStartButton = (Button) findViewById(R.id.countStartButton);
        countStopButton = (Button) findViewById(R.id.countStopButton);
        countText = (TextView) findViewById(R.id.countText);
        
        countStartButton.setOnClickListener()
        {
            public void onClick(View v)
            {
                if(timer == null){
                    timer = new Timer();
                    timer.scheduleAtFixedRate(new TimerTask()
                    {
                        public void run()
                        {
                            timeInMills += 100;
                            countText.setText(Long.toString(timeInMills) 
                                    + "mill seconds");
                        }
                    }, 0, 100);
                }
            }
        }
        
        countStopButton.setOnClickListener()
        {
            public void onClick(View v)
            {
                timeInMills = 0;
                if(timer != null){
                    timer.cancel();
                    timer = null;
                }
            }
        }
    }
}

Timerはインスタンスの再利用ができないのでカウントを再開するたびに新しいTimerオブジェクトを作る必要があります。

また、TimerTaskを定期的に実行するにはschedulescheduleAtFixedRateの2つのメソッドがあり、どっちも役割が同じように見えて実は次の違いがあります。

もしタスクをscheduleの場合は直前のタスクに依存してしまうので、正確にカウントするにはscheduleAtFixedRateを使った方が良いみたいです。

関連項目
プライバシーポリシー