Custom Countdown Timer in Java/Android






3.67/5 (5 votes)
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 whentick
event is generated at a periodic interval,onFinish
is invoked when the duration for timer has elapsed andonCancel
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 ofTimerRunnable
identifies the state of timer and acts accordingly.- If the state is cancelled, it invokes
onCancel
method of theTimerTickListener
callback and shuts down the scheduler. - If the duration has elapsed, it invokes
onFinish
method of theTimerTickListener
callback and shuts down the scheduler. - Else it simply invokes the
onTick
method of theTimerTickListener
callback passing in the time left for the timer to expire.
- If the state is cancelled, it invokes
Constructor
- Apart from setting the various instance variables, taking a callback implementation forTimerTickListener
and saving it with itself, what it does significantly, is creating aSingleThreadScheduledExecutor
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 therun
method of an inner runnable classTimerRunnable
.onCancel
- This puts the timer into cancelled stateextendTimer
- This extends the duration oftimer
with the amount passed in to this method.
/**
* 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;
}
}