I know it's old but it's something that you will always need. I wrote a class called TimedTaskExecutor because I needed to run a specific command every x milliseconds for a unknown amount of time. One of my top priorities was to make x as accurate as can be. I tried with AsyncTask, Handlers and CountdownTimer but all gave me bad results.
Here is the class:
package com.example.myapp;
import java.util.concurrent.TimeUnit;
/*
* MUST RUN IN BACKGROUND THREAD
*/
public class TimedTaskExecutor {
// ------------------------------ FIELDS ------------------------------
/**
*
*/
private double intervalInMilliseconds;
/**
*
*/
private IVoidEmptyCallback callback;
/**
*
*/
private long sleepInterval;
// --------------------------- CONSTRUCTORS ---------------------------
/**
* @param intervalInMilliseconds
* @param callback
*/
public TimedTaskExecutor(double intervalInMilliseconds, IVoidEmptyCallback callback, long sleepInterval) {
this.intervalInMilliseconds = intervalInMilliseconds;
this.callback = callback;
this.sleepInterval = sleepInterval;
}
// --------------------- GETTER / SETTER METHODS ---------------------
/**
* @return
*/
private IVoidEmptyCallback getCallback() {
return callback;
}
/**
* @return
*/
private double getIntervalInMilliseconds() {
return intervalInMilliseconds;
}
/**
* @return
*/
private long getSleepInterval() {
return sleepInterval;
}
// -------------------------- OTHER METHODS --------------------------
/**
*
*/
public void run(ICallback<Boolean> isRunningChecker) {
long nanosInterval = (long) (getIntervalInMilliseconds() * 1000000);
Long previousNanos = null;
while (isRunningChecker.callback()) {
long nanos = TimeUnit.NANOSECONDS.toNanos(System.nanoTime());
if (previousNanos == null || (double) (nanos - previousNanos) >= nanosInterval) {
getCallback().callback();
if (previousNanos != null) {
// Removing the difference
previousNanos = nanos - (nanos - previousNanos - nanosInterval);
} else {
previousNanos = nanos;
}
}
if (getSleepInterval() > 0) {
try {
Thread.sleep(getSleepInterval());
} catch (InterruptedException ignore) {
}
}
}
}
// -------------------------- INNER CLASSES --------------------------
/**
*
*/
public interface IVoidEmptyCallback {
/**
*
*/
public void callback();
}
/**
* @param <T>
*/
public interface ICallback<T> {
/**
* @return
*/
public T callback();
}
}
Here is an example of how to use it:
private boolean running;
Handler handler = new Handler();
handler.postDelayed(
new Runnable() {
/**
*
*/
@Override
public void run() {
running = false;
}
},
5000
);
HandlerThread handlerThread = new HandlerThread("For background");
handlerThread.start();
Handler background = new Handler(handlerThread.getLooper());
background.post(
new Runnable() {
/**
*
*/
@Override
public void run() {
new TimedTaskExecutor(
10, // Run tick every 10 milliseconds
// The callback for each tick
new TimedTaskExecutor.IVoidEmptyCallback() {
/**
*
*/
private int counter = 1;
/**
*
*/
@Override
public void callback() {
// You can use the handler to post runnables to the UI
Log.d("runTimedTask", String.valueOf(counter++));
}
},
// sleep interval in order to allow the CPU to rest
2
).run(
// A callback to check when to stop
new TimedTaskExecutor.ICallback<Boolean>() {
/**
*
* @return
*/
@Override
public Boolean callback() {
return running;
}
}
);
}
}
);
Running this code will produce 500 calls with more or less accurate x. (lowering the sleep factor make it more accurate)
- Edit: It seems that in Nexus 5 with Lollipop you should use 0 in the sleep factor.