AlarmManager 仅在设备重启时触发

AlarmManager only firing when device is restarted

我有一个每周发送通知的应用程序。它应该在星期四早上 8:15 开火;然而,我在重启我的 phone 后(昨天 8:15 AM 之后)本周才收到更新。警报在第一个 运行 的 MainActivity 中设置,我实现了 BootReceiver,以便在用户关闭其 phone 时重置警报。有谁知道我是否在某处遗漏了逻辑错误?

编辑:现在每次重启都会触发。我该如何解决这个问题?通知功能的方式使得在每次重启时发送通知成为一个大问题(它会增加用于兑换奖励的积分值)。

这是 MainActivity 中的代码部分:

public void manageNotifications() {
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(java.util.Calendar.DAY_OF_WEEK, java.util.Calendar.THURSDAY);
    calendar.set(java.util.Calendar.HOUR_OF_DAY, 8);
    calendar.set(java.util.Calendar.MINUTE, 15);

    /*
    sets alarm manager to go off at 8:15 in the morning every 7 days on Thursday
     */
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 60 * 24 * 7, pendingIntent);
}//end manageNotifications

这是我的 BootReceiver class:

public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent alarmIntent = new Intent(context, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(java.util.Calendar.DAY_OF_WEEK, java.util.Calendar.THURSDAY);
        calendar.set(java.util.Calendar.HOUR_OF_DAY, 8);
        calendar.set(java.util.Calendar.MINUTE, 15);

    /*
    sets alarm manager to go off at 8:15 in the morning every 7 days on Thursday
     */
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 60 * 24 * 7, pendingIntent);
    }
}

}

这是在第一个 运行 上从 BootReceiver 和 MainActivity 调用的 AlarmReceiver 代码:

public class AlarmReceiver extends BroadcastReceiver {

NotificationCompat.Builder notificationBuilder;
Intent notificationResultIntent;
ArrayList<Show> shows;
SharedPreferences spSpreadsheets, spNotifications;
final String showSpreadsheetURL = "https://docs.google.com/spreadsheets/d/1Ax2-gUY33i_pRHZIwR8AULy6-nbnAbM8Qm5-CGISevc/gviz/tq";

public void onReceive(Context context, Intent intent) {
    System.out.println("AlarmReceiver created");
    spNotifications = context.getSharedPreferences("notificationToggle", Context.MODE_PRIVATE);
    spSpreadsheets = context.getSharedPreferences("spreadsheets", Context.MODE_PRIVATE);

    if (spNotifications.getBoolean("notifications", false)) {
        int nextRegularShowIndex = 0, i = 0;

        shows = new ArrayList<>();

        try {
            if (spSpreadsheets.getString("showsSpreadsheet", "").equals(""))
                getShows(context);
            shows = processShowsJson(new JSONObject(spSpreadsheets.getString("showsSpreadsheet", "")));

            while (shows.get(i).getShowTime() != 0) {
            /*
            checks for first instance of a regular showtime
             */
                i++;
                nextRegularShowIndex = i;
            }
            checkForPastShows();
            notificationBuilder = new NotificationCompat.Builder(context)
                    .setSmallIcon(R.drawable.applogo)
                    .setContentTitle("Comedy Club of Jacksonville")
                    .setContentText(shows.get(nextRegularShowIndex).getComedian() + " headlines this weekend at the Comedy " +
                            "Club of Jacksonville.  Click to read more.")
                    .setDefaults(Notification.DEFAULT_ALL);
            notificationResultIntent = new Intent(context, ThisWeekendFromNotification.class).putParcelableArrayListExtra("shows", shows);

            TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
            stackBuilder.addParentStack(ThisWeekend.class);
            stackBuilder.addNextIntent(notificationResultIntent);
            PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
            notificationBuilder.setContentIntent(resultPendingIntent);
            notificationBuilder.setAutoCancel(true);
            NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            mNotificationManager.notify(0, notificationBuilder.build());
            System.out.println("Notification built");

        } catch (Exception e) {
            e.printStackTrace();
        }//end onReceive
    }
}

根据我的 reddit 评论跟进:看起来你肯定是在过去设置闹钟。 Calendarapi有点奇怪。

    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(java.util.Calendar.DAY_OF_WEEK, java.util.Calendar.THURSDAY);
    calendar.set(java.util.Calendar.HOUR_OF_DAY, 8);
    calendar.set(java.util.Calendar.MINUTE, 15);

此代码会将日历设置为星期四 8:15 上午 当前周 。因此,如果用户在星期五早上重新启动 phone,则此时间将指向 之前的 星期四早上。根据此处 setRepeating() 的文档,这将导致 AlarmManager 立即触发您的警报:https://developer.android.com/reference/android/app/AlarmManager.html

If the stated trigger time is in the past, the alarm will be triggered immediately, with an alarm count depending on how far in the past the trigger time is relative to the repeat interval.

编辑——跟进您对如何修复的评论:

快速而肮脏的方法是这样的

long alarmTime = calendar.getTimeInMillis();
if (alarmTime < System.currentTimeMillis()) {
    alarmTime += DateUtils.WEEK_IN_MILLIS;
}

alarmManager.set( ... alarmTime ... );

我以前也讨厌在询问有关 Java 日历 api 的问题时听到这个,但是使用 Joda-Time 库(或者在你的情况下 Joda-Time Android)将节省长的你很痛苦运行。 api 更加清晰易用。