How to reduce Thread.sleep () delay in Android

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(); // takes 200 millisecs or less long eventWait = eventTime - (System.nanoTime() / 100000L); Thread.sleep(eventWait / 10L); listener.onEvent(); eventTime = eventTime + loopTime; } } 

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.

+4
source share
1 answer

I use delayed messages with Handler for a similar purpose. It might be a bit overkill. In this case, I would look at the Timer class.

 mTimer = new Timer(); mTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { Log.v("TEST", " tick"); } }, 0, 250); 

This gives me latency + -2 milliseconds in the emulator.

+2
source

All Articles