计时器在Android中的睡眠状态期间不会精确到期

时间:2017-11-24 11:51:39

标签: android c android-alarms ipsec android-kernel

我们正在尝试在android中实现本机代码的计时器。计时器应该在唤醒和睡眠模式下正常工作。当计时器到期时,我们的本机代码将向网络发送DPD(死对等检测)消息 我们尝试了以下方法。

  1. 在用户空间代码中使用Android框架API用于警报管理器,结果如下:

    • 即使在2s,3s,5s等小型定时器的唤醒状态下,也无法获得准确的结果。
    • 也不适合睡眠模式。
  2. 我们尝试在内核空间代码中使用内核计时器,结果如下:

    • 适用于唤醒状态。
    • 但是对于睡眠状态,定时器根本不会过期。当我们手动唤醒设备时,定时器就会过期。因此,总的来说内核定时器在睡眠状态下不起作用。
  3. 3.使用唤醒锁定 *我们试图避免使用唤醒锁,因为它可能会导致严重的性能问题

    PS - 开源Ipsec实现strongswan即使在睡眠模式下也会发送DPD消息的精确时间。但似乎strongswan不使用唤醒锁,所以我们仍然试图弄清楚它在睡眠模式下是如何工作的。任何人都在搜索回答这个问题可能想要查看那段代码。

    任何人都可以建议一些方法来解决这个问题。

3 个答案:

答案 0 :(得分:0)

当Android进入睡眠状态时,它会有几个状态,最后一个状态是冻结所有进程并关闭CPU。 在那种情况下,你的时间不会被激发。您必须创建一个唤醒内核并设置唤醒锁定的事件,以便cpu不会再次关闭。这可以使用android报警来完成。

答案 1 :(得分:0)

让计时器在睡眠模式下正常工作的唯一方法是使用 Wakelock 保持设备部分清醒。但请确保您的应用程序确实需要计时器一直工作,因为文档说,

使用此API会严重影响设备电池寿命。除非确实需要,否则不要获得PowerManager.WakeLocks,尽可能使用最低级别,并确保尽快释放它们。

浏览PowerManager类,使用以下代码获取部分唤醒锁

  PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
  PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
  wl.acquire();
  ..cpu will stay on during this section..
  wl.release();

答案 2 :(得分:-1)

启动计时器,当应用程序进入后台时,启动alarmManager。再次,如果应用程序来到前台并且计时器未到期,那么它将重新触发计时器并将删除警报管理器。

private int timeToStart;
private TimerState timerState;

 private static final int MAX_TIME = 60;  //Time length is 60 seconds

private enum TimerState {
    STOPPED,
    RUNNING

}


private void initTimer() {
    Log.e(TAG,"initTimer called");

    long startTime = mPrefs.getStartedTime(); //here mprefs is your shared preference manager
    if (startTime > 0) {
        timeToStart = (int) (MAX_TIME - (getNow() - startTime));
        if (timeToStart <= 0) {
            // TIMER EXPIRED
            onTimerFinish();
        } else {
            startTimer();
            timerState = TimerState.RUNNING;
        }
    } else {
        timeToStart = MAX_TIME;
        timerState = TimerState.STOPPED;
    }
}

    private long getNow() {
    Calendar rightNow = Calendar.getInstance();
    return rightNow.getTimeInMillis() / 1000;
}

    private void onTimerFinish() {
    Log.e(TAG,"onTimerFinish() called");

    timerState = TimerState.STOPPED;
    mPrefs.setStartedTime(0);
    timeToStart = MAX_TIME;

}

private void startTimer() {
    Log.e(TAG,"startTimer() called");

    countDownTimer = new CountDownTimer(timeToStart * 1000, 1000) {

        @Override
        public void onTick(long millisUntilFinished) {
            timeToStart -= 1;
        }

        @Override
        public void onFinish() {
            onTimerFinish();
        }
    }.start();
}

    public void setAlarmManager() {
    int wakeUpTime = (mPrefs.getStartedTime() + MAX_TIME) * 1000;
    AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(this, TimeReceiver.class);
    PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        am.setAlarmClock(new AlarmManager.AlarmClockInfo(wakeUpTime, sender), sender);
    } else {
        am.set(AlarmManager.RTC_WAKEUP, wakeUpTime, sender);
    }
}

    public void removeAlarmManager() {
    Intent intent = new Intent(this, TimeReceiver.class);
    PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    am.cancel(sender);
}

    @Override
    protected void onResume() {
    super.onResume();
        initTimer();
        removeAlarmManager();

}