Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Custom Countdown Timer in Java/Android

3.67/5 (5 votes)
17 Apr 2016CPOL3 min read 20.4K  
A custom count down timer in java/android which allows to change the timer dynamically

Introduction

For one of my requirements for an Android app, I encountered a need for a countdown timer in which timer value could be modified without recreating the timer. For example, if a countdown timer is first created for a duration of 10 minutes, during the course of processing on user’s/app logic demand, I should be able to modify this duration from 10 minutes to say, 14 minutes. Android provides an efficient CountDownTimer class which is good as a plain vanilla Timer but does not provide this particular feature for modifying the duration. If required, you need to cancel the current timer instance and then create another one with the altered duration.

Background

According to me, this requirement is so simple, easy to code and quite a frequent use case, that it should be provided as a standard class. During my search, I found it is not available anywhere. I thought of creating a CountDownTimer which provides this feature. I would not like to reinvent the wheel and hence took the CountDownTimer class provided by Android for the basic structure with some basic differences. The class provided by Android uses Handlers to generate timer events whereas the proposed class uses ScheduledExectuorService to do so.

Using the Code

The code shown below is developed for Android. But this could be used for a Java application also, with minor tweakings by removing the constructs like Handler which are Android specific.

As expected, countdown timer provides the following behaviours to caller:

  • Ability to start the timer for an assigned duration
  • Ability to cancel the timer, if required, before the assigned duration
  • Should stop by itself when the assigned duration elapses
  • Ability to generate events and hence invoke a callback periodically at an interval specified by caller

In addition to these functionalites, this countdown timer also provides for:

  • Ability to extend the duration while the timer is still going on

Description of Code

  • TimerTickListener - The implementation for this interface is supposed to be provided to the timer during construction. It provides callback methods which are invoked in various situations, i.e., onTick is invoked when tick event is generated at a periodic interval, onFinish is invoked when the duration for timer has elapsed and onCancel is invoked when the timer is forcefully canceled.
  • TimerRunnable - This is the core of timer which provides the code which is executed by the scheduler at regular intervals. Here, we have put in the logic for timer to behave in the fashion we want according to the current state of timer. You must have noticed that this code is doing it in the main thread by posting this execution to the main thread handler. This was a requirement for my application to do it in the main thread, you may choose to perform this in the same thread depending on your requirement.
  • The run method of TimerRunnable identifies the state of timer and acts accordingly.
    • If the state is cancelled, it invokes onCancel method of the TimerTickListener callback and shuts down the scheduler.
    • If the duration has elapsed, it invokes onFinish method of the TimerTickListener callback and shuts down the scheduler.
    • Else it simply invokes the onTick method of the TimerTickListener callback passing in the time left for the timer to expire.
  • Constructor - Apart from setting the various instance variables, taking a callback implementation for TimerTickListener and saving it with itself, what it does significantly, is creating a SingleThreadScheduledExecutor assigned to instance variable scheduler.
  • Start - When this method is invoked, the scheduler created in the constructor is scheduled to run at a periodic time interval passed in constructor and invokes the run method of an inner runnable class TimerRunnable.
  • onCancel - This puts the timer into cancelled state
  • extendTimer - This extends the duration of timer with the amount passed in to this method.
Java
/**
 * Implementation of a timer which allows the duration of timer to be modified dynamically
 */
public class CustomCountDownTimer {

    //thread on which the callbacks will be called
    private Handler mainThreadHandler = new Handler(Looper.getMainLooper());

    //listener interface which is to be implemented by the users of the count down timer
    public interface TimerTickListener{
        /**
         * Callback on each tick
         * @param millisLeft time left in millisec for the timer to shutdown
         */
        public void onTick(long millisLeft);

        /**
         * Callback to be invokded when timer's time finishes
         */
        public void onFinish();

        /**
         * Callback to be invokded when timer is canceled
         */
        public void onCancel();
    }

    /**
     * Inner class which delegates the events to callbacks provided in the TimerTickListener
     */
    private class TimerRunnable implements Runnable{
        public void run(){
            mainThreadHandler.post(new Runnable() {
                long millisLeft = stopTimeInFuture - SystemClock.elapsedRealtime();
                @Override
                public void run() {
                    if (isCancelled){
                        //shutdown the scheduler
                        tickListener.onCancel();
                        scheduler.shutdown();
                    }
                    else if (millisLeft <= 0) {
                        tickListener.onFinish();
                        scheduler.shutdown();
                    }
                    else{
                        tickListener.onTick(millisLeft);
                    }
                }
            });
        }
    }    

    //Millis since epoch when alarm should stop.
    private long millisInFuture;

    //The interval in millis that the user receives callbacks
    private final long countdownInterval;

    //the time at which timer is to stop
    private long stopTimeInFuture;

    //boolean representing if the timer was cancelled
    private boolean isCancelled = false;

    //listener which listens to the timer events
    private TimerTickListener tickListener;

    //scheduler which provides the thread to create timer
    private ScheduledExecutorService scheduler;

    /**
     * Constructor
     * @param millisInFuture time in millisec for which timer is to run
     * @param countDownInterval interval frequency in millisec at which the callback will be invoked
     * @param tickListener implementation of TimerTickListener which provides callbacks code
     */
    public CustomCountDownTimer(long millisInFuture, long countDownInterval,
                                     TimerTickListener tickListener) {
        this.millisInFuture = millisInFuture;
        stopTimeInFuture = SystemClock.elapsedRealtime() + this.millisInFuture;
        countdownInterval = countDownInterval;
        this.tickListener = tickListener;
        scheduler = Executors.newSingleThreadScheduledExecutor();
    }

    /**
     * Start the countdown.
     */
    public synchronized void start() {
        isCancelled = false;
        scheduler.scheduleWithFixedDelay(new TimerRunnable(), 0, countdownInterval,
                TimeUnit.MILLISECONDS);
    }

    /**
     * Cancels the countdown timer
     */
    public synchronized final void cancel() {
        isCancelled = true;
    }

    /**
     * Extends the time of the countdown timer
     * @param delta time in millisec by which timer is to be extended
     */
    public void extendTime(long delta){
        stopTimeInFuture = stopTimeInFuture + delta;
        millisInFuture = millisInFuture + delta;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)