Android timer that works when device is sleeping

2020-02-25 08:09发布

I'm writing a sports app that needs to track the elapsed time of quarter/half/period. Elapsed time needs to be accurate to the second. The game clock needs to continue to run even if the user explicitly places the device in sleep mode by pressing the power button.

My first attempt at this involved using Handler.postDelayed() to trigger the clock ticks every 200ms and WindowManager.LayoutParms.FLAG_KEEP_SCREEN_ON to ensure that the "clock" wasn't stopped by a screen timeout. But I soon learned that it was possible to circumvent this approach by pressing the power button to manually put the device to sleep. In addition, the postDelayed() approach is experiencing some clock drift, apparently a result of the time spent in the run() method. The actual numbers are still accurate, but instead of being aligned, for example, on 5 second boundaries which are easily understood by users - the timers involved start to drift, resulting in some understandable user confusion.

After a bit of research I found techiques for using services, java timers, AlarmManager, and PartialWakeLock to implement timers. Services by themselves won't solve the problem associated with the device going to sleep. Java timers, like services, don't solve the problem with the device going to sleep. AlarmManager seems like a good approach, but I'm concerned that this isn't an appropriate use of AlarmManager (i.e., very short intervals between alarms). Using PartialWakeLock also looks promising, but by itself it doesn't address the clock-drift problem I'm experiencing.

I'm going to try a combination of AlarmManager and PartialWakeLock. The idea is that AlarmManager will help combat clock-drift and PartialWakeLock to help keep the code simple (fingers-crossed). I'm hoping that this approach will result in a reasonable balance between power conservation, code complexity, and user expectations. Any advice is greatly appreciated.

Thanks,

Rich

2条回答
▲ chillily
2楼-- · 2020-02-25 08:36

I've got a partial solution to my original post above. It doesn't yet address the clock drift associated with the time spent in calculations during the postDelayed() processing, but it is a step forward. In addition, it's deceptively simple, always a good sign.

It turns out I was using SystemClock.uptimeMillis() when I should have been using SystemClock.elapsedRealtime(). The difference between the 2 is subtle, but important.

As you might expect, my solution keeps track of elapsed time by accumulating durations between calls to postDelayed() - i.e., elapsed time = elapsedTime + lastClockInterval. As stated above, the original implementation used uptimeMillis(). Careful reading of the javadoc reveals that uptimeMillis() doesn't include time spent in "deep sleep", e.g., when the user presses the power button. But the elapsedRealtime() method does include time spent in "deep sleep" mode. All that was required to track time across deep sleep cycles was to replace the use of uptimeMillis() with elapsedRealtime(). Success! No need to use AlarmManager, PartialWakeLock, or anything else substantially more complicated. Granted, these methods still have uses, but they are overkill when implementing a simple elapsed-time clock or timer.

The next problem to tackle is with the clock-drift caused by the non-zero execution time associated with postDelayed() processing. I'm hoping that spawning a thread to do the processing will address this issue, allowing postDelayed() to more or less mimic an asynchronous call. Another approach would be to adjust the postDelayed() delay time to take into account the time spent in postDelayed(). I'll post my results.

On an unrelated note, during my investigation I treated myself to a CommonsWare Warescription. While I didn't directly use any ideas from this source for this problem, I do think that it is going to be my Android go-to information source for the foreseeable future. I've got an O'Reilly subscription through my day job, but I've found the CommonsWare books to be as least as good, if not better, source of information about Android development as the O'Reilly resources. And I have found the O'Reilly Safari resources to be pretty good. Interesting...

Cheers, Rich

查看更多
smile是对你的礼貌
3楼-- · 2020-02-25 08:42

I'm sorry this is not quite an answer, but I feel your process is similar to mine, but there was little in regards to code to make it clear how you got around the sleep issue. I don't have drift, but the app does hang when it goes into sleep mode,then kind of resets forward when the display is active, then hangs again when the device sleeps. This is the core of the timer process.

    Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {

    @Override
    public void run() {

        // do something here to display

        processTime();    // process what to be done on a sec by sec basis
        try {
            timerHandler.postDelayed(this, 1000
        } catch (Exception ex){

        }

    }
};

Is there something here that I can do to allow it to continue when in sleep mode? This use to work on older version of android/devices.

查看更多
登录 后发表回答