I am generating sync events in a loop in a stream (non-UI) in an Android app, and I need these events to occur at exact time intervals (the exact one here means they don't change more than +/- 5 milliseconds). Any errors within +/- 10 milliseconds (and, of course, +/- 20 milliseconds) can be perceived by the user. At the top of this loop, I do some other calculations that take a variable amount of time, but at the bottom of the loop I need the event to happen at a pre-calculated time.
A simplified version (without exception handling) of one attempt for my non-UI thread is below:
public final void run() { long loopTime = 2500L; long eventTime = (System.nanoTime() / 100000L) + loopTime; while (true) { calcutionsTakingVaryingAmountOfTime();
This is a call to listener.onEvent()
, which must be exactly synchronized.
In the above example, the temporary variables loopTime
, eventTime
and eventWait
measure time in tenths of a millisecond. Expressions (System.nanoTime() / 100000L)
that measure the current time are also in tenths of a millisecond.
I am absolutely sure that calcutionsTakingVaryingAmountOfTime()
always takes less than 200 milliseconds, and the call to listener.onEvent()
is only a few milliseconds. So if loopTime
set to 2500L
, my events should happen every 250 milliseconds.
I instructed my code (not shown) to print on Log.d()
delay in Thread.sleep()
the wake-up time. That is, I calculate
long latency = (System.nanoTime() / 100000L) - eventTime
immediately after returning from Thread.sleep()
and print it before Log.d()
.
When I run this in the emulator, what I find is that latency
(after dividing by 10 to get the result in milliseconds) usually goes between 1 and 50 milliseconds in consecutive passes through the loop, with random values as high as half a second . When launched on the device itself, everything gets a little better, but still a little shaky (and even still, the behavior of the emulator makes me wonder if this will happen on users' devices).
To try to reinforce my event and control the delay, I tried several other approaches:
Instead of calling Thread.sleep(eventWait / 10L)
, with calling this.wait(eventWait / 10L)
(completely inappropriate use of wait (), I know)
I manipulated the priorities of the thread before entering the loop by calling Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO)
, as is done in all Android libraries.
But there was no improvement in latency with this.
One approach that supports the event and reduces the delay to less than 2 or 3 milliseconds, and rarely hiccups, is to replace the call to Thread.sleep()
with a polling cycle:
while ((System.nanoTime() / 100000L) < eventTime) ;
On the one hand, I feel embarrassed doing machine cycles like a drunken sailor at large. On the other hand, I am starting to think that there is no better way, and I have to burn the machine cycles in the polling loop in order to reduce my delay and fulfill my specification. Of course, when my application goes to the background, I pause my thread, so this polling cycle works. But what a waste.
We will be very grateful for any ideas.