Android8/26/2020

How to Build a Countdown Timer in Android Using CountDownTimer

Countdown timers are commonly used in Android applications for:

  • OTP verification screens
  • Quiz applications
  • Workout timers
  • Pomodoro productivity apps
  • Game countdown systems

Android provides a built-in class called CountDownTimer that helps developers implement countdown functionality easily.

In this tutorial, we will learn how to build a Countdown Timer in Android using the CountDownTimer class.


What Is CountDownTimer?

CountDownTimer is an Android utility class used to create timers that count down over a fixed interval.

It provides two important callback methods:

  • onTick() → Called at regular intervals
  • onFinish() → Called when timer completes

What We Will Build

In this Android example:

  • User can start the timer
  • User can pause the timer
  • User can reset the timer
  • Remaining time updates dynamically

Step 1 — Create activity_main.xml

Create the UI layout file.


<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_view_countdown"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="10:00"
        android:textColor="@android:color/black"
        android:textSize="60sp"/>

    <Button
        android:id="@+id/button_start_pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text_view_countdown"
        android:layout_centerHorizontal="true"
        android:text="Start"/>

    <Button
        android:id="@+id/button_reset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text_view_countdown"
        android:layout_marginStart="12dp"
        android:layout_toEndOf="@id/button_start_pause"
        android:text="Reset"
        android:visibility="gone"/>

</RelativeLayout>

Step 2 — Implement MainActivity.java

Open MainActivity.java and add the following code.


package com.example.countdowntimer;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private static final long START_TIME_IN_MILLIS =
            600000;

    private TextView textViewCountDown;

    private Button buttonStartPause;

    private Button buttonReset;

    private CountDownTimer countDownTimer;

    private boolean timerRunning;

    private long timeLeftInMillis =
            START_TIME_IN_MILLIS;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        textViewCountDown =
                findViewById(R.id.text_view_countdown);

        buttonStartPause =
                findViewById(R.id.button_start_pause);

        buttonReset =
                findViewById(R.id.button_reset);

        buttonStartPause.setOnClickListener(
                new View.OnClickListener() {

                    @Override
                    public void onClick(View view) {

                        if (timerRunning) {

                            pauseTimer();

                        } else {

                            startTimer();
                        }
                    }
                });

        buttonReset.setOnClickListener(
                new View.OnClickListener() {

                    @Override
                    public void onClick(View view) {

                        resetTimer();
                    }
                });

        updateCountDownText();
    }

    private void startTimer() {

        countDownTimer =
                new CountDownTimer(
                        timeLeftInMillis,
                        1000
                ) {

                    @Override
                    public void onTick(
                            long millisUntilFinished
                    ) {

                        timeLeftInMillis =
                                millisUntilFinished;

                        updateCountDownText();
                    }

                    @Override
                    public void onFinish() {

                        timerRunning = false;

                        buttonStartPause.setText(
                                "Start"
                        );

                        buttonStartPause.setVisibility(
                                View.INVISIBLE
                        );

                        buttonReset.setVisibility(
                                View.VISIBLE
                        );
                    }
                }.start();

        timerRunning = true;

        buttonStartPause.setText("Pause");

        buttonReset.setVisibility(View.INVISIBLE);
    }

    private void pauseTimer() {

        countDownTimer.cancel();

        timerRunning = false;

        buttonStartPause.setText("Start");

        buttonReset.setVisibility(View.VISIBLE);
    }

    private void resetTimer() {

        timeLeftInMillis =
                START_TIME_IN_MILLIS;

        updateCountDownText();

        buttonReset.setVisibility(View.INVISIBLE);

        buttonStartPause.setVisibility(View.VISIBLE);
    }

    private void updateCountDownText() {

        int minutes =
                (int) (timeLeftInMillis / 1000) / 60;

        int seconds =
                (int) (timeLeftInMillis / 1000) % 60;

        String timeFormatted =
                String.format(
                        Locale.getDefault(),
                        "%02d:%02d",
                        minutes,
                        seconds
                );

        textViewCountDown.setText(timeFormatted);
    }
}

How This Countdown Timer Works

The workflow is:

  1. User clicks Start button
  2. CountDownTimer begins counting
  3. onTick() updates remaining time every second
  4. User can pause or reset timer
  5. onFinish() triggers after countdown completes

Understanding CountDownTimer Parameters


new CountDownTimer(totalTime, interval)
Parameter Description
totalTime Total countdown duration
interval Update interval frequency

How Time Formatting Works

The timer stores values in milliseconds.

We convert milliseconds into:

  • Minutes
  • Seconds

Then format the output using:


String.format("%02d:%02d", minutes, seconds)

Common Mistakes Developers Make

1. Forgetting to Cancel Timer

Timers should be cancelled when no longer needed to avoid memory leaks.


2. Updating UI Incorrectly

UI updates should happen safely inside timer callbacks.


3. Not Handling Activity Lifecycle

Timers may continue running during Activity destruction if lifecycle handling is ignored.


Modern Android Improvements

Modern Android applications can improve this implementation using:

  • Kotlin
  • Coroutines
  • Jetpack Compose
  • ViewModel
  • Lifecycle-aware timers

CountDownTimer vs Coroutines

CountDownTimer Coroutines
Simple built-in solution Modern asynchronous approach
Limited lifecycle support Lifecycle-aware architecture
Good for basic timers Better for scalable apps

FAQ

Can CountDownTimer run in background?

CountDownTimer is tied to the application process and is not ideal for long-running background timers.

Can I create custom intervals?

Yes. Developers can update timers at any interval such as 500ms or 1 second.

What is the modern alternative?

Kotlin Coroutines combined with ViewModel is the preferred modern solution.


Conclusion

Countdown timers are useful for many Android application features including OTP systems, quizzes, and productivity apps.

The CountDownTimer class provides a simple way to implement countdown functionality with regular UI updates.

Modern Android applications should combine timers with lifecycle-aware architecture for better scalability and reliability.


About the Author

Salil Jha is a Full Stack and Mobile Developer with experience in Android, React Native, scalable SaaS systems, fintech applications, and developer tooling platforms.

CodeChain Dev — Build Modern Products. Solve Real Problems.

Deep Structural Diagnostics.

Mastering JSON is only the first step. Use our industrial-grade workbench to format, validate, and synthesize models for your production APIs.