3

Edit:

This is not talking about precision issues, from the code and log below, you can see that I requested to sleep for 1 second, but the result was almost 200 seconds, sometimes it could jump to 600 seconds, this cannot be a precision issue..


I was using handlerthread before and sometimes the job posted to handler just does not start on time, to get more details I changed it to the basic Thread, and it turns out the Thread.sleep() is the issue, but I'm not sure how to fix this, what could be the possible reasons?

hGpsThread = new Thread(mGpsWorker);
hGpsThread.start();

private final Runnable mGpsWorker = new Runnable() {
    @Override
    public void run() {
        long lastGpsRequestTime = 0;
        l.Write("GPS thread started.");
        while (isRunning) {
            l.Write("GPS thread loop start.");
            try {
                long currentTimeMillis = System.currentTimeMillis();
                if (currentTimeMillis >= lastGpsRequestTime + gpsUpdateInterval) {
                    l.Write("Requesting location update");
                    gpslib.getLocation();
                    lastGpsRequestTime = currentTimeMillis;
                }
                l.Write("GPS thread before sleep");
                Thread.sleep(1000);
                l.Write("GPS thread after sleep");
            } catch (InterruptedException e) {
            }
            l.Write("GPS thread loop end.");
        }
        l.Write("GPS thread ended.");
    }
};

NOTE: getLocation() calls requestLocationUpdates using a looper from another thread, so location updating should not affect this thread, as I understand.

getLocation() also creates a Timer and schedules a timeout, could that be the issue? as I understand it should not be an issue.

This is the log: (this only happens occasionally, say the chance is close to 0.5%)

Wed Jul 10 11:45:46 AEST 2013 GPS thread loop start.
Wed Jul 10 11:45:46 AEST 2013 GPS thread before sleep
Wed Jul 10 11:49:04 AEST 2013 GPS thread after sleep
Wed Jul 10 11:49:04 AEST 2013 GPS thread loop end.

Thanks

The testing environment is: HTC Aria, Android 2.2 And looks like it only happens when running on battery, but my app doesn't act differently on charging status.

agou
  • 728
  • 1
  • 10
  • 24
  • Does it happen when you comment out the `getLocation()`? Looks like another thread is not giving up the CPU, once this one enters `sleep`. – Aert Jul 10 '13 at 02:41
  • @Aert, thanks, I'm monitoring the CPU usage now, if I found something, I'll update the question, thanks. – agou Jul 10 '13 at 02:44
  • Are you doing this when the Android display is off by power button or display time out? – Royston Pinto Jul 10 '13 at 03:43
  • @RoystonPinto yes, the above code is from a foreground service class. And actually it does look like it only happens when the display is off! This could also explain why it didn't happen when connected to power because I set the display to always ON when connected to power... so is there any difference? – agou Jul 10 '13 at 04:11
  • 1
    Yes, when the display is off, Handlers and Threads are suspended until when woken up either by some wakelock in another application or when the display is turned on. Hence the random behavior you observe is correct. If you need proper behavior when display is off, switch to AlarmManager as this works even when display is off. – Royston Pinto Jul 10 '13 at 05:56
  • @RoystonPinto, seems this should be the reason, thank you very much! – agou Jul 10 '13 at 07:12
  • Let me put it up as an answer for others to see :) you welcome! – Royston Pinto Jul 10 '13 at 08:15

3 Answers3

9

Basically Thread.sleep() and Handlers work fine as long as the screen is on (implies Android should not be in a deep sleep state/screen off state).

When the Screen is turned off, default Android behavior is to suspend Thread.sleep() and Handlers() until the screen is turned back on again or if it is woken up by some application grabbing a wakelock. Hence your application will work perfectly if the screen is on throughout, but when it is turned off, it will behave erratically.

The best work around is to switch to AlarmManager because when the Alarm is triggered, onRecieve() always grabs a wakelock by default causing Android to wake up and execute.

Royston Pinto
  • 6,681
  • 2
  • 28
  • 46
  • Thank you. I have a few questions: Does that apply only to threads created directly by UI thread or also the ones created by a Service/IntentService set as Foreground? Does that apply also to wait(timeout)? Does the suggested AlarmManager necessarily light up the screen? (I need an operation run every 10 seconds.) Can you provide link to documentation of the issue? Again, thanks a lot. – GozzoMan Apr 01 '15 at 15:09
0

The sleep() documentation warns that it's not precise:

The precision is not guaranteed - the Thread may sleep more or less than requested.

There is a number of questions related to the lack of precision of sleep(), like this one, this one and a few more. Google for "java thread sleep precision" or "android thread sleep precision".

Community
  • 1
  • 1
Christian Garbin
  • 2,512
  • 1
  • 23
  • 31
  • Thanks for the reply, but it's not about precision, I requested to sleep for 1 second, the result is almost 200 seconds, sometimes it could jump to 600 seconds, this cannot be a precision issue... – agou Jul 10 '13 at 02:35
  • I agree your numbers vary quiet a bit, but the description of the API makes absolutely no promises, especially the part that says `the Thread may sleep more or less than requested`. if you find a more useful piece of information on how to sleep for a precise amount, post as an answer and accept it. It's fair game to do so. – Christian Garbin Jul 11 '13 at 00:57
-1

I also hat that problem, not that it was not precise enough, but instead of 10 seconds, the Thread waited for 30 minutes - for an application, which runs over weeks and which has to perform requests periodically, this is no solution. Therefore, I made the BetterSleeper :-)

import java.sql.Timestamp;

public class BetterSleeper {

    public static void sleepSeconds(int seconds_to_wait) {
        System.out.println("START WAIT FOR "+seconds_to_wait+" SECONDS");
        BetterSleeper.sleepMillis(seconds_to_wait*1000);
        System.out.println("END WAIT");
    }

    public static void sleepMillis(int milliseconds_to_wait) {
        System.out.println("START WAIT FOR "+milliseconds_to_wait+" MILLISECONDS");
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        long start_milliseconds = timestamp.getTime();
        long end_milliseconds = start_milliseconds + milliseconds_to_wait;
        while (true) {
            Timestamp endtime = new Timestamp(System.currentTimeMillis());
            if ((endtime.getTime()) >= end_milliseconds) {
                break;
            }
        }
        System.out.println("END WAIT");
    }
}

This actually proves to be much more accurate, if you take into consideration that processing time in this snippet actually takes almost no effect, because it is remembered when it started and when it should stop right at the beginning.

Another advantage is you don't have all the disadvantages of a dispatched sleeping thread like with Thread.sleep, where background threads are being abandonned if you terminate the main thread.


NOTE I had nothing to do with Android development - my problem occurred on a Windows PC running Windows 10.

Florian Müller
  • 7,448
  • 25
  • 78
  • 120