Android AlarmManager在不同时间触发

Android AlarmManager在不同时间触发,android,alarmmanager,Android,Alarmmanager,我已经创建并部署到Google Play alarm应用程序。和往常一样,我在8个不同的真实设备(我不使用emulator)上执行了大量测试,这些设备运行不同的api级别、根/非根、不同的时区以及其他功能 问题是,在点击超过10000次下载后,一些用户抱怨警报在指定时间前1小时触发,而另一些用户(数量的4倍)抱怨警报在1小时后触发。我从世界上不同的地方得到这个,我也从相同的地方得到了5颗星。 我立刻认为我在代码的某些地方错误地使用了时区,但事实上我只是在使用 GregorianCalendar

我已经创建并部署到Google Play alarm应用程序。和往常一样,我在8个不同的真实设备(我不使用emulator)上执行了大量测试,这些设备运行不同的api级别、根/非根、不同的时区以及其他功能

问题是,在点击超过10000次下载后,一些用户抱怨警报在指定时间前1小时触发,而另一些用户(数量的4倍)抱怨警报在1小时后触发。我从世界上不同的地方得到这个,我也从相同的地方得到了5颗星。 我立刻认为我在代码的某些地方错误地使用了时区,但事实上我只是在使用

GregorianCalendar gc = new GregorianCalendar();
这种实例化是我在所有代码中使用的。我使用系统AlarmManager设置报警,如:

    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context.getApplicationContext(), AlarmReceiver.class);
    GregorianCalendar gc = new GregorianCalendar();
    long triggerAtMillis = 0;
    if(nextAlarm != null){
        triggerAtMillis = System.currentTimeMillis() 
                + 60*1000*alarmDifference(nextAlarm, gc.get(Calendar.HOUR_OF_DAY), gc.get(Calendar.MINUTE), true)
                - 1000*gc.get(Calendar.SECOND);

    }else{
//          Log.d("", "nextAlarm: NULL");
    return;
    }

    PendingIntent operation = PendingIntent.getBroadcast(context.getApplicationContext(), REQUEST_CODE_ALARM_BROADCAST, intent, 0);
    am.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, operation);
alarmDifference方法非常简单:

public static int alarmDifference(Alarm alarm, int hour, int minute, boolean inverse) {
    int hours = hour - alarm.getHour();
    if(inverse){
        hours *= -1;
    }
    if(hours < 0)
        hours += 24;

    int minutes = minute - alarm.getMinute();
    if(inverse){
        minutes *= -1;
    }

    if(hours==0 && minutes<0){
        hours = 23; //this happens if we set the time to the current hour
    }

    if(minutes < 0)
        minutes += 60;

//  Log.d("", "Time left: "+ hours+" hours, " + minutes+" minutes");
    return hours*60 + minutes;

}
公共静态整数报警差异(报警、整数小时、整数分钟、布尔倒数){
int hours=hour-alarm.getHour();
if(逆){
小时数*=-1;
}
如果(小时<0)
小时+=24小时;
int minutes=minute-alarm.getMinute();
if(逆){
分钟*=-1;
}

如果(小时==0&&minutes我很确定问题出在这一行:

triggerAtMillis = System.currentTimeMillis() 
                + 60*1000*alarmDifference(nextAlarm, gc.get(Calendar.HOUR_OF_DAY), gc.get(Calendar.MINUTE), true)
                - 1000*gc.get(Calendar.SECOND);
此操作不能保证以原子方式进行。换句话说

  • gc.get(日历小时/天)
  • gc.get(日历分钟)
可以在不同的时间发生,例如,当时钟从14:59:99切换到15:00:00时,您可能会得到14小时和00分钟,或者15小时和59分钟


相反,我将从一个不可变的时间戳值中提取小时/分钟/秒。

您可以尝试使用“下一步”查找问题:


在应用程序中,从internet服务加载世界时间(例如)并将时间与设备上的时区进行比较。您可以确定什么是问题:在设备中全局还是在您的应用程序中。

您是在检查AM和PM吗?请注意,在代码中,我使用的是Calendar.HOUR OF_DAY;这是24小时格式,因此我不需要检查AM/PM。我将尝试此方法,但根据Google analytics,用户没有进行设置时间是XX:59,也不是XX:00。我会让你知道的。GregorianCalendar对象gc在实例化后不会更改,因此它是不可变的。报警差异是使用POJO自定义报警对象计算的,因此如果系统时间更改,triggerAtMillis值不会更改。应用程序应该脱机工作,因此这是无效的。谢谢nyways。您还可以尝试使用GPS(NMEA)数据比较时间(这一次并非总是正确的)。这不需要internet权限,但需要位置权限。这种方式有更多缺点,因为用户可能禁用了位置,或者在一段时间内无法获得位置更新。不起作用。