Home » Android » Android Games » Android Game Development – Measuring FPS

About Impaler

Android Game Development – Measuring FPS

In the previous entry we have created a game loop that runs at a constant speed and constant (more or less) FPS.

How can we measure it?

Check the new MainThread.java class.

package net.obviam.droidz;

import java.text.DecimalFormat;

import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

 * @author impaler
 * The Main thread which contains the game loop. The thread must have access to
 * the surface view and holder to trigger events every game tick.
public class MainThread extends Thread {

	private static final String TAG = MainThread.class.getSimpleName();

	// desired fps
	private final static int 	MAX_FPS = 50;
	// maximum number of frames to be skipped
	private final static int	MAX_FRAME_SKIPS = 5;
	// the frame period
	private final static int	FRAME_PERIOD = 1000 / MAX_FPS;

	// Stuff for stats */
    private DecimalFormat df = new DecimalFormat("0.##");  // 2 dp
	// we'll be reading the stats every second
	private final static int 	STAT_INTERVAL = 1000; //ms
	// the average will be calculated by storing
	// the last n FPSs
	private final static int	FPS_HISTORY_NR = 10;
	// last time the status was stored
	private long lastStatusStore = 0;
	// the status time counter
	private long statusIntervalTimer	= 0l;
	// number of frames skipped since the game started
	private long totalFramesSkipped			= 0l;
	// number of frames skipped in a store cycle (1 sec)
	private long framesSkippedPerStatCycle 	= 0l;

	// number of rendered frames in an interval
	private int frameCountPerStatCycle = 0;
	private long totalFrameCount = 0l;
	// the last FPS values
	private double 	fpsStore[];
	// the number of times the stat has been read
	private long 	statsCount = 0;
	// the average FPS since the game started
	private double 	averageFps = 0.0;

	// Surface holder that can access the physical surface
	private SurfaceHolder surfaceHolder;
	// The actual view that handles inputs
	// and draws to the surface
	private MainGamePanel gamePanel;

	// flag to hold game state
	private boolean running;
	public void setRunning(boolean running) {
		this.running = running;

	public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
		this.surfaceHolder = surfaceHolder;
		this.gamePanel = gamePanel;

	public void run() {
		Canvas canvas;
		Log.d(TAG, "Starting game loop");
		// initialise timing elements for stat gathering

		long beginTime;		// the time when the cycle begun
		long timeDiff;		// the time it took for the cycle to execute
		int sleepTime;		// ms to sleep (<0 if we're behind)
		int framesSkipped;	// number of frames being skipped 

		sleepTime = 0;

		while (running) {
			canvas = null;
			// try locking the canvas for exclusive pixel editing
			// in the surface
			try {
				canvas = this.surfaceHolder.lockCanvas();
				synchronized (surfaceHolder) {
					beginTime = System.currentTimeMillis();
					framesSkipped = 0;	// resetting the frames skipped
					// update game state
					// render state to the screen
					// draws the canvas on the panel
					// calculate how long did the cycle take
					timeDiff = System.currentTimeMillis() - beginTime;
					// calculate sleep time
					sleepTime = (int)(FRAME_PERIOD - timeDiff);

					if (sleepTime > 0) {
						// if sleepTime > 0 we're OK
						try {
							// send the thread to sleep for a short period
							// very useful for battery saving
						} catch (InterruptedException e) {}

					while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
						// we need to catch up
						this.gamePanel.update(); // update without rendering
						sleepTime += FRAME_PERIOD;	// add frame period to check if in next frame

					if (framesSkipped > 0) {
						Log.d(TAG, "Skipped:" + framesSkipped);
					// for statistics
					framesSkippedPerStatCycle += framesSkipped;
					// calling the routine to store the gathered statistics
			} finally {
				// in case of an exception the surface is not left in
				// an inconsistent state
				if (canvas != null) {
			}	// end finally

	 * The statistics - it is called every cycle, it checks if time since last
	 * store is greater than the statistics gathering period (1 sec) and if so
	 * it calculates the FPS for the last period and stores it.
	 *  It tracks the number of frames per period. The number of frames since
	 *  the start of the period are summed up and the calculation takes part
	 *  only if the next period and the frame count is reset to 0.
	private void storeStats() {

		// check the actual time
		statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);

		if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
			// calculate the actual frames pers status check interval
			double actualFps = (double)(frameCountPerStatCycle / (STAT_INTERVAL / 1000));

			//stores the latest fps in the array
			fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;

			// increase the number of times statistics was calculated

			double totalFps = 0.0;
			// sum up the stored fps values
			for (int i = 0; i < FPS_HISTORY_NR; i++) {
				totalFps += fpsStore[i];

			// obtain the average
			if (statsCount < FPS_HISTORY_NR) {
				// in case of the first 10 triggers
				averageFps = totalFps / statsCount;
			} else {
				averageFps = totalFps / FPS_HISTORY_NR;
			// saving the number of total frames skipped
			totalFramesSkipped += framesSkippedPerStatCycle;
			// resetting the counters after a status record (1 sec)
			framesSkippedPerStatCycle = 0;
			statusIntervalTimer = 0;
			frameCountPerStatCycle = 0;

			statusIntervalTimer = System.currentTimeMillis();
			lastStatusStore = statusIntervalTimer;
//			Log.d(TAG, "Average FPS:" + df.format(averageFps));
			gamePanel.setAvgFps("FPS: " + df.format(averageFps));

	private void initTimingElements() {
		// initialise timing elements
		fpsStore = new double[FPS_HISTORY_NR];
		for (int i = 0; i < FPS_HISTORY_NR; i++) {
			fpsStore[i] = 0.0;
		Log.d(TAG + ".initTimingElements()", "Timing elements for stats initialised");


I introduced a simple measuring function. I count the number of frames every second and store them in the fpsStore[] array. The storeStats() is called every tick and if the 1 second interval (STAT_INTERVAL = 1000;) is not reached then it simply adds the number of frames to the existing count.
If the one second is hit then it takes the number of rendered frames and adds them to the array of FPSs. After this I just reset the counters for the current statistics cycle and add the results to a global counter. The average is calculated on the values stored in the last 10 seconds.
Line 171 logs the FPS every second while line 172 sets the avgFps value of the gamePanel instance to be displayed on the screen.

The MainGamePanel.java class’s render method contains the the displayFps call which just draws the text onto the top right corner of the display every time the state is rendered. It also has a private member that is set from the thread.

// the fps to be displayed
private String avgFps;
public void setAvgFps(String avgFps) {
	this.avgFps = avgFps;

public void render(Canvas canvas) {
	// display fps
	displayFps(canvas, avgFps);

private void displayFps(Canvas canvas, String fps) {
	if (canvas != null && fps != null) {
		Paint paint = new Paint();
		paint.setARGB(255, 255, 255, 255);
		canvas.drawText(fps, this.getWidth() - 50, 20, paint);

Try running it. You should have the FPS displayed in the top right corner.

FPS displayed

Reference: Measuring FPS from our JCG partner Tamas Jano from “Against The Grain” blog.

Do not forget to check out our new Android Game ArkDroid (screenshots below). You feedback will be more than helpful!
Related Articles:

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!


1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design


and many more ....




  1. I’m also having an issue with the FPS.

    I’m using the 4.0 emulator and I’m topping at ~9.5 FPS. My code is the same as yours though.

  2. For the emulator, apparently the act of setting the canvas to null and initializing it “canvas = surfaceHolder.lockCanvas();” takes quite some time. In the original code, it is not considered part of the frame period when it should be. Put “beginTime = System.currentTimeMillis();” right underneath the “while(running) {” line and it will show you that you need to skip a lot of frames because the emulator is slow, as expected. If you leave the beginTime where it is, it makes you think you are faster than 50 fps because it sleeps about 15ms and only takes 5ms to update and render. This, however, is leaving out a huge piece of overhead. After making my suggested change, my timeDiff is always over 20 (the frame period). It’s normally around 110 and sometimes drops to 55, but I am skipping 5 frames most cycles and keeping 10-12 FPS.

  3. System.currentTimeMillis()  uses the system clock, so if the game is paused and later resumed, the repeat calls to update in the inner while loop will cause it to totally wig out.
      e.g. while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {

  4. This is a great code, but I faced a little flickering issue. So I changed the location of the while loop given below after the finally block and it worked fine.

    Please let me know if this is right.

    while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {

Leave a Reply

Your email address will not be published. Required fields are marked *


Want to take your Java Skills to the next level?
Grab our programming books for FREE!
  • Save time by leveraging our field-tested solutions to common problems.
  • The books cover a wide range of topics, from JPA and JUnit, to JMeter and Android.
  • Each book comes as a standalone guide (with source code provided), so that you use it as reference.
Last Step ...

Where should we send the free eBooks?

Good Work!
To download the books, please verify your email address by following the instructions found on the email we just sent you.