Search code examples
androidmultithreadingandroid-handler

Handler doesn't work correctly


first of all excuse me if my title doesn't describe my question very well but i couldn't find a better one .

there is a simple stopWatch app that has three button start,stop,reset and a textview to display time . app has just one activity like this:

public class StopwatchActivity extends AppCompatActivity {

private int mNumberOfSeconds = 0;
private boolean mRunning = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_stopwatch);
    //if if uncomment this runner method and delete the runner inside onClickStart everything will work find 
    //runner()

}

public void onClickStart(View view){
    mRunning = true;
    runner();
}

public void onClickStop(View view){
    mRunning = false;
}

public void onClickReset(View view){
    mRunning = false;
    mNumberOfSeconds = 0;

}

public void runner(){
    final TextView timeView = (TextView) findViewById(R.id.time_view);
    final Handler handler = new Handler();
    handler.post(new Runnable() {
        @Override
        public void run() {
            int hours = mNumberOfSeconds/3600;
            int minutes = (mNumberOfSeconds%3600)/60;
            int second = mNumberOfSeconds%60;
            String time = String.format("%d:%02d:%02d" , hours , minutes , second );
            timeView.setText(time);
            if (mRunning){
                mNumberOfSeconds++;
            }
            handler.postDelayed(this , 1000);
        }
    });

}
}

my problem is when i comment the runner() in onClickStart method and put it in the onCreate method everything is ok . but when i change the code like above the code is still running but after i press stop button and then press start again the second will increment by 4 or 5 very fast. can anyone explain me what is the difference between this two modes?


Solution

  • declare your handler globally

    public void runner(){
        timeView = (TextView) findViewById(R.id.time_view);
        handler = new Handler();
        runnable = new Runnable() {
            @Override
            public void run() {
                int hours = mNumberOfSeconds/3600;
                int minutes = (mNumberOfSeconds%3600)/60;
                int second = mNumberOfSeconds%60;
                String time = String.format("%d:%02d:%02d" , hours , minutes , second );
                timeView.setText(time);
                if (mRunning){
                    mNumberOfSeconds++;
                }
                handler.postDelayed(this , 1000);
            }
        }
        handler.post(runnable);
    
    }
    

    in button function

    public void onClickStart(View view){
        if(handler != null) {
            //restart the handler to avoid duplicate runnable 
            handler.removeCallbacks(runnable);//or this handler.removeCallbacksAndMessages(null);
        }
        mRunning = true;
        runner();
    }
    
    public void onClickStop(View view){
        mRunning = false;
        handler.removeCallbacks(runnable); // this will stop the handler from working
    }