user2386986
user2386986

Reputation: 119

Why does my simple timer app keep getting faster each time I pause?

OK, so initially I thought the problem was that each time I press stop and then start, a new runnable was created, so we will have multiple runnable running at the same time, thus updating the timer quicker and quicker each time. But it doesn't seems to be the problem: I changed the code by adding a boolean "running" so that pressing start/stop only changes the boolean variable. In this way, I think only one runnable is running. But it still gets faster as I click start/stop several times in a row. Can someone tell me where the problem is?

package com.example.timernew;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    Handler h=new Handler();
    TextView t;
    Button start, stop,reset;
    boolean running;
    Runnable run=new Runnable() { 
            @Override public void run() { updateTime(); } };;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        t = (TextView) findViewById(R.id.TextArea); 
        start = (Button) findViewById(R.id.start_button); 
        stop=(Button) findViewById(R.id.stop_button);
        running=false;
        h.post(run);

            start.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v){
                if(running==false){
                running=true;
                }
            }
        });
        stop.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v){
                if(running==true){
                running=false;
                }
            }
        });

    } 
    public void updateTime() {
        if(running==true){
        t.setText("" + (Integer.parseInt(t.getText().toString()) +1)); 
        h.postDelayed(run, 1000);}
    }

}

Upvotes: 0

Views: 197

Answers (1)

ImmortalDev
ImmortalDev

Reputation: 476

I added a call to updateTime() in the start button OnClickListenter and have had no problems with the count continuing after hitting stop. Even after waiting 10+ seconds, hitting start again just starts continues where it left off.

I did notice that the value would increase twice in quick succession if I hit start within 1 second of hitting stop. This is because hitting start calls updateTime() and the previously posted runnable's delay has not finished, so when it does, it also increments the time.

A better bet would be to use an AsyncTask:

    package com.example.timernew;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    Handler h=new Handler();
    TextView t;
    Button start, stop,reset;
    volatile boolean running;
    UpdateTimeTask mUpdateTimeTask;
    int counter = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        t = (TextView) findViewById(R.id.TextArea); 
        start = (Button) findViewById(R.id.start_button); 
        stop=(Button) findViewById(R.id.stop_button);
        running=false;

        start.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v){
                if (null == mUpdateTimeTask) {
                    mUpdateTimeTask = new UpdateTimeTask();
                    mUpdateTimeTask.execute();
                }
            }
        });

        stop.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v){
                if(null != mUpdateTimeTask) {
                    mUpdateTimeTask.cancel(true);
                    mUpdateTimeTask = null;
                }
            }
        });

    } 

    public class UpdateTimeTask extends AsyncTask<Void, Void, Void> { 

        protected Void doInBackground(Void... params) {

            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ie) {
                    Log.e("timer app", "a problem occurred while sleeping or the task was cancelled", ie);
                    return null;
                }
                counter++;
                publishProgress();
            }
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            t.setText(String.valueOf(counter));
        }
    }
}

To answer your question about changing the boolean value and calling updateTime():

In your original code, updateTime() is only called during onCreate() at this line:

h.post(run);

And since you have the boolean set to false, it does nothing and finishes running.

Now, suppose you add the call to updateTime() inside the start_button click listener and hit start, stop, start all in quick succession. Here's what's happening (assuming those each happen at 100ms intervals):

0ms
click start sets the boolean to true, calls updateTime()

10ms
updateTime() is called, the boolean is true, so it changes the text and posts a runnable to be run in 1000ms

100ms click stop sets the boolean to false

200ms click start sets the boolean to true, calls updateTime()

210ms updateTime() is called, the boolean is true, so it changes the text and posts a runnable to be run in 1000ms

1010ms the first posted runnable runs, sees that the boolean is true, changes the text and posts another runnable to be run in 1000ms

1210ms the second posted runnable runs, sees that the boolean is true, changes the text, and posts another runnable

At this point you are less than 1.3 seconds in and the count is already at 3 seconds. Now, the text will be changed approximately every n + .1 and n + .2 seconds from now.

Upvotes: 1

Related Questions