Android睡眠时也能工作的定时器

13

我正在编写一款体育应用程序,需要跟踪节/半场/周期经过的时间。经过的时间需要精确到秒。即使用户通过按下电源按钮明确将设备置于睡眠模式下,游戏时钟也需要继续运行。

我首次尝试使用Handler.postDelayed()触发时钟每200毫秒进行滴答声并使用WindowManager.LayoutParms.FLAG_KEEP_SCREEN_ON确保“时钟”不会被屏幕超时停止。但我很快发现可以通过按电源按钮手动将设备置于睡眠状态来规避此方法。此外,postDelayed()方法存在一些时钟漂移问题,似乎是由于在run()方法中花费的时间所致。实际数字仍然准确,但与用户轻松理解的例如5秒边界对齐相比,所涉及的计时器开始漂移,导致某些可以理解的用户困惑。

经过一番研究,我发现可使用服务、java定时器AlarmManagerPartialWakeLock技术来实现计时器。服务本身无法解决设备进入睡眠状态的问题。像服务一样,Java定时器也不能解决设备进入睡眠状态的问题。AlarmManager似乎是一个很好的方法,但我担心这不是AlarmManager的适当用法(即,在警报之间非常短的时间间隔)。单独使用PartialWakeLock看起来很有前途,但它本身并不能解决我正在遇到的时钟漂移问题。

我将尝试结合使用AlarmManager和PartialWakeLock。其想法是AlarmManager将有助于抵消时钟漂移,PartialWakeLock将有助于保持代码简单(手指交叉)。我希望这种方法将在节约电力、代码复杂度和用户期望之间取得合理平衡。非常感谢任何建议。

感谢您,Rich。

使用PartialWakeLock,您将不会看到任何漂移,但是您的电池将会耗尽。 - NitZRobotKoder
你说过“经过时间需要精确到秒”,为什么不使用简单的系统时间(例如System.currentTimeMillis())呢?只需在开始时保存时间戳,然后在需要时从当前时间中减去即可。 - Dmitry
2个回答

4
我已经针对我上面的原始帖子提出了部分解决方案。它尚未解决与postDelayed()处理过程中计算所花费时间相关的时钟漂移问题,但这是一个向前迈进的一步。此外,它看起来非常简单,这总是一个好迹象。
事实证明,当我使用SystemClock.uptimeMillis()时,我应该使用SystemClock.elapsedRealtime()。这两者之间的区别微妙但重要。
正如您所预期的那样,我的解决方案通过在调用postDelayed()之间累积持续时间来跟踪经过的时间 - 即,经过的时间= elapsedTime + lastClockInterval。如上所述,原始实现使用了uptimeMillis()。仔细阅读javadoc可发现,uptimeMillis()不包括在“深度睡眠”中花费的时间,例如当用户按下电源按钮时。但是elapsedRealtime()方法包括在“深度睡眠”模式下花费的时间。跨越深度睡眠周期跟踪时间所需的全部工作就是将uptimeMillis()的使用替换为elapsedRealtime()。成功!在实现简单的经过时间时钟或计时器时,无需使用AlarmManager、PartialWakeLock或任何其他复杂得多的东西。尽管这些方法仍然有用途,但它们在实现简单的经过时间时钟或计时器时就显得过于复杂了。
下一个需要解决的问题是由于postDelayed()处理过程中存在非零执行时间所导致的时钟漂移问题。我希望通过生成线程来解决这个问题,使postDelayed()可以更或多或少地模拟异步调用。另一种方法是调整postDelayed()延迟时间,以考虑在postDelayed()中花费的时间。我会发布我的结果。
另外,值得一提的是,在我的调研过程中,我给自己买了一个CommonsWare Warescription。虽然我没有直接从这个资源中使用任何想法来解决这个问题,但我认为它将成为我未来 Android 开发的信息来源。我有一个通过我的日常工作获得的 O'Reilly 订阅,但我发现 CommonsWare 的书籍与 O'Reilly 资源一样好,甚至更好。而且我发现 O'Reilly Safari 资源也很不错。有趣...
祝好, Rich

0

非常抱歉这不是一个完整的答案,但我觉得你的过程与我的类似,但是关于如何解决睡眠问题的代码方面很少,让人难以理解。我没有漂移,但是当应用程序进入睡眠模式时会挂起,然后在显示器激活时向前重置,然后在设备休眠时再次挂起。这是计时器过程的核心。

    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){

        }

    }
};

有什么方法可以让它在睡眠模式下继续运行吗?这在旧版的安卓/设备上曾经有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接