Повторяющиеся уведомления на Android 4

Цель: уведомление появляется один раз каждый день, в 14:00, если выполняется определенное условие.

Пример: Для простоты будем считать, что условие, проверяемое при подключении к Интернету, выполняется каждый день. Если сегодня уже после 14:00, мы начнем уведомления с завтрашнего дня. Например, пользователь запускает приложение в 16:00 в понедельник и получает уведомления во вторник в 14:00, в среду в 14:00, в четверг в 14:00 и так далее.

Проблема. В 14:00 приходит первое уведомление, но затем я получаю одно и то же уведомление снова и снова в случайное время.

Похоже, проблема только на Android >= 4.0. На более ранних версиях Android все работает хорошо.

Вот как я отправляю уведомление:

public class NotifyService extends Service
{       
static final int NOTIFICATION_ID = 1;
// ...

@Override
public IBinder onBind(Intent intent)
{
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    try
    {
        Symbol biggest = getBiggestMover();
        if (biggest != null)
        {
            String title = getString(R.string.app_name);
            String text = getNotificationText(biggest.symbol, biggest.change);
            sendNotification(title, text);
        }
    }
    catch (Exception e)
    {
        // If there is Internet problem we do nothing, don't want to disturb the user.
        e.printStackTrace();
    }

    return super.onStartCommand(intent, flags, startId);
}

/** @return Symbol which is the biggest mover today. If there is no big mover - null is returned.
 * @throws Exception If there is Internet problem. */
private Symbol getBiggestMover() throws Exception
{
    Symbol biggest = null;
    Symbol[] equities = Network.getTraded(SymbolType.EQUITY);
    for (Symbol equity : equities)
    {
        if (Utilities.isToday(equity.lastTraded) && isBigMove(equity.change) && isBigger(equity, biggest))
        {
            biggest = equity;
        }
    }
    return biggest;
}   

private void sendNotification(String title, String text)
{
    Notification notification = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
    notification.flags = Notification.FLAG_AUTO_CANCEL;

    Intent clickIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);

    notification.setLatestEventInfo(this, title, text, pendingIntent);

    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    manager.notify(NOTIFICATION_ID, notification);
}   
// ...  
}

sendNotification() вызывается в 14:00 из-за AlarmManager:

public class ServiceStarter extends BroadcastReceiver
{

@Override
public void onReceive(Context context, Intent intent)
{
    setNotificationAlarm(context);
}

/** Set repeating notifications every 24 hours. */
public static void setNotificationAlarm(Context context)
{
    Intent intent = new Intent(context, NotifyService.class);
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

    final int oneDay = 24 * 60 * 60 * 1000;
    alarmManager.setRepeating(AlarmManager.RTC, getTriggerTime(), oneDay, pendingIntent);
}

private static long getTriggerTime()
{
    GregorianCalendar calendar = new GregorianCalendar();
    calendar.set(GregorianCalendar.HOUR_OF_DAY, 14);
    calendar.set(GregorianCalendar.MINUTE, 0);
    calendar.set(GregorianCalendar.SECOND, 0);
    calendar.set(GregorianCalendar.MILLISECOND, 0);

    if (calendar.before(new GregorianCalendar()))
    {
        calendar.add(GregorianCalendar.DAY_OF_MONTH, 1);
    }

    return calendar.getTimeInMillis();
}

}

setNotificationAlarm() вызывается из 2-х мест. Во-первых, в начале приложения. Во-вторых, из кода выше, когда телефон перезагружается (onReceive() получает BOOT_COMPLETED). Я делаю это, потому что когда пользователь выключает телефон, AlarmManager сбрасывает свои сигналы тревоги.

Так что все должно работать, потому что alarmManager.setRepeating() переопределяет предыдущую тревогу.

Я обнаружил, что у кого-то была такая же проблема, но также нет ответа:
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/t_tDU4PwR3g

Также здесь я нашел аналогичную проблему: http://comments.gmane.org/gmane.comp.handhelds.android.devel/171471

Некоторое время назад я спрашивал, как создавать такие уведомления, так что это связано:
Ежедневные уведомления в определенное время


person Community    schedule 12.10.2012    source источник
comment
В sendNotification замените getApplication() на this, замените getBaseContext() на this и замените Intent.FLAG_ACTIVITY_NEW_TASK на что-то, что там принадлежит, поскольку флаги Intent не идут в статических методах на PendingIntent. Кроме того, не используйте RTC_WAKEUP с getService() PendingIntent, так как это ненадежно — используйте мой WakefulIntentService или что-то еще, что работает от getBroadcast() PendingIntent.   -  person CommonsWare    schedule 13.10.2012
comment
Кроме того, используйте adb shell dumpsys alarm для проверки запланированных сигналов тревоги, используйте точки останова, чтобы увидеть, что запускает ваш метод sendNotification(), и т. д.   -  person CommonsWare    schedule 13.10.2012
comment
Большое спасибо @CommonsWare, я последовал вашим советам. Однако проблема по-прежнему возникает на мобильных устройствах с Android 4. Возможно ли, что в их реализации API есть какая-то ошибка? Потому что я читал, что в Android 4 появились расширенные уведомления, поэтому они немного изменили код.   -  person Adam Stelmaszczyk    schedule 23.10.2012
comment
Является ли это возможным? Конечно. Это вероятно? Нет. Вы представили доказательства этого? Нет. Создайте и опубликуйте полный проект, демонстрирующий проблему, включая полные инструкции по использованию проекта для демонстрации проблемы. Кроме того, имейте в виду, что расширенные уведомления были добавлены в 4.1, и вы утверждаете, что ваша проблема возникла в 4.0.   -  person CommonsWare    schedule 23.10.2012
comment
У меня похожая проблема с моим приложением Follow Now (play.google.com /store/search?q=follow+now&c=apps). Как и у вас, у меня такая проблема только с Андроидом ›= 4 - После первого уведомления остальные не издают вибрацию и звук. Но уведомление выскакивает и просто уходит.   -  person Felipe    schedule 02.11.2012


Ответы (2)


Используйте AlarmManager.RTC_WAKEUP вместо AlarmManager.RTC

В AlarmManager.RTC

Время будильника в System.currentTimeMillis() (время настенных часов в формате UTC). Этот будильник не будит устройство; если он отключится, когда устройство спит, оно не будет доставлено до следующего пробуждения устройства.

где как в AlarmManager.RTC_WAKEUP

Время будильника в System.currentTimeMillis() (время настенных часов в формате UTC), которое разбудит устройство, когда оно отключится.

person Community    schedule 30.10.2012
comment
Это неправильно, согласно первому комментарию к моему сообщению, сделанному @CommonsWare. Кроме того, не используйте RTC_WAKEUP с PendingIntent getService(), так как это ненадежно. - person Adam Stelmaszczyk; 30.10.2012

Была такая же проблема на устройстве ICS+. Мой обходной путь был очень простым-> Поместите текущее время в общие настройки при отображении уведомления. Перед этим всегда проверяйте, действительно ли пройден интервал, а если нет, то просто прерывайте.

            long lastnotification = sharedPrefs.getLong("lnnd", -1);
            Calendar now = Calendar.getInstance();
            if (!namedayalarmEnabled) {
                    return;
            }
            if (lastnotification > 1) {
                    Calendar last = Calendar.getInstance();
                    last.setTimeInMillis(lastnotification);
                    long distance = (now.getTimeInMillis() - last
                                    .getTimeInMillis());
                    if (distance < YOURINTERVAL) {
                            return;
                    } else {
                            SharedPreferences.Editor editor = sharedPrefs.edit();
                            editor.putLong("lnnd", now.getTimeInMillis());
                            editor.commit();
                    }
            }
person Community    schedule 01.11.2012