Отправка уведомлений через AlarmManager и BroadcastReceiver не работает? - PullRequest
0 голосов
/ 25 августа 2018

Я планирую уведомление на 5 дней вперед, поэтому я создаю сигнал тревоги с помощью AlarmManager, который запускает PendingIntent, который запускает мой BroadcastReceiver.

Если я попробую код в течение 10 секунд, он работает. Когда я пробую это в течение 5 дней, ничего не происходит.

Класс NotificationScheduler является вспомогательным классом для установки и обновления сигналов тревоги.

Даты стрельбы верны, так как я храню их в базе данных, и я уже проверял их.

Manifest:

<receiver android:name=".reminder.ReminderReceiver" />

NotificationScheduler:

class NotificationScheduler {

    companion object {

        const val NOTIFICATION_EXTRA_CLAIM_ID = "notification_extra_bookentry_id"

        const val INTENT_ACTION_REMINDER = "at.guger.moneybook.reminder"

        fun setReminder(context: Context, bookEntryId: Long, fireDate: Date? = null) {
            val mCalendar = Calendar.getInstance()

            val lFireDate = if (fireDate == null) {
                mCalendar.timeInMillis += 5 * 24 * 3600 * 1000
                mCalendar.set(Calendar.HOUR_OF_DAY, 12)

                mCalendar.time
            } else {
                fireDate
            }

            create(context, bookEntryId, lFireDate.time)

            AppDatabase.getInstance(context).reminderDao().insert(Reminder(bookEntryId, lFireDate))
        }

        fun updateReminder(context: Context, bookEntryId: Long) {
            cancel(context, bookEntryId)

            val mCalendar = Calendar.getInstance()
            mCalendar.timeInMillis += 5 * 24 * 3600 * 1000
            mCalendar.set(Calendar.HOUR_OF_DAY, 12)

            create(context, bookEntryId, mCalendar.timeInMillis)

            AppDatabase.getInstance(context).reminderDao().update(Reminder(bookEntryId, mCalendar.time))
        }

        fun cancelReminder(context: Context, bookEntryId: Long) {
            cancel(context, bookEntryId)

            AppDatabase.getInstance(context).reminderDao().delete(Reminder(bookEntryId))
        }

        private fun create(context: Context, bookEntryId: Long, fireDate: Long) {
            val mAlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

            val mComponentName = ComponentName(context, ReminderReceiver::class.java)
            context.packageManager.setComponentEnabledSetting(mComponentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)

            val mIntent = Intent(context, ReminderReceiver::class.java)
            mIntent.action = INTENT_ACTION_REMINDER
            mIntent.putExtra(NOTIFICATION_EXTRA_CLAIM_ID, bookEntryId)

            val mPendingIntent = PendingIntent.getBroadcast(context, bookEntryId.toInt(), mIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            if (Utils.isKitKat()) {
                mAlarmManager.setWindow(AlarmManager.RTC, fireDate, AlarmManager.INTERVAL_HOUR, mPendingIntent)
            } else {
                mAlarmManager.set(AlarmManager.RTC, fireDate, mPendingIntent)
            }
        }

        private fun cancel(context: Context, bookEntryId: Long) {
            val mAlarmManager: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

            val mComponentName = ComponentName(context, ReminderReceiver::class.java)
            context.packageManager.setComponentEnabledSetting(mComponentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)

            val mIntent = Intent(context, ReminderReceiver::class.java)
            mIntent.putExtra(NOTIFICATION_EXTRA_CLAIM_ID, bookEntryId)

            val mPendingIntent = PendingIntent.getBroadcast(context, bookEntryId.toInt(), mIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            mAlarmManager.cancel(mPendingIntent)
            mPendingIntent.cancel()
        }
    }
}

BroadcastReceiver:

class ReminderReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        if (context != null && intent != null) {
            when (intent.action) {
                NotificationScheduler.INTENT_ACTION_REMINDER -> {
                    val mPowerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
                    val mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this::class.simpleName)

                    mWakeLock.acquire(WAKELOCK_TIME)

                    val iClaimEntryId = intent.getLongExtra(NotificationScheduler.NOTIFICATION_EXTRA_CLAIM_ID, -1)

                    showNotification(context, iClaimEntryId)
                    AppDatabase.getInstance(context).reminderDao().delete(Reminder(iClaimEntryId))

                    mWakeLock.release()
                }
            }
        }
    }

    private fun showNotification(context: Context, claimEntryId: Long) {
        val mNotificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val nBuilder: Notification.Builder

        if (Utils.isOreo()) {
            val mNotificationChannel = NotificationChannel(NOTIFICATIONCHANNEL_CLAIMREMINDERID, context.getString(R.string.notificationchannel_claimreminder_title), NotificationManager.IMPORTANCE_DEFAULT)
            mNotificationChannel.description = context.getString(R.string.notificationchannel_claimreminder_description)

            mNotificationManager.createNotificationChannel(mNotificationChannel)

            nBuilder = Notification.Builder(context, NOTIFICATIONCHANNEL_CLAIMREMINDERID)
        } else {
            nBuilder = Notification.Builder(context)
        }

        val mClaimEntry: BookEntry = AppDatabase.getInstance(context).bookEntryDao().get(claimEntryId)

        val mCurrencyFormatter = DecimalFormat.getCurrencyInstance(Preferences.getInstance(context).currency.locale)

        nBuilder.setSmallIcon(R.drawable.ic_money)
        nBuilder.setContentTitle(context.getString(R.string.notification_claimreminder_title, mCurrencyFormatter.format(mClaimEntry.dValue)))
        val sContacts = mClaimEntry.getContacts(context).joinToString().takeIf { it.isNotEmpty() }
                ?: "-"
        nBuilder.setContentText(context.getString(R.string.notification_claimreminder_content, sContacts))
        nBuilder.setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
        nBuilder.setAutoCancel(true)

        mNotificationManager.notify(mClaimEntry.lId!!.toInt(), nBuilder.build())
    }

    companion object {
        const val NOTIFICATIONCHANNEL_CLAIMREMINDERID = "notification_channel_claimreminder"

        const val WAKELOCK_TIME: Long = 1000
    }
}

Ответы [ 3 ]

0 голосов
/ 04 сентября 2018

Скорее всего, это тот случай, когда ваше устройство переходит в режим ожидания, а диспетчер тревог не запускает запланированный будильник. Попробуйте использовать (это код Java):

AlarmManager alarmManager = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
    }
    else{
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
        }
    }

Посмотрите на setExactAndAllowWhileIdle

0 голосов
/ 05 сентября 2018

У меня были проблемы с AlarmManager, поэтому я сейчас использую WorkManager.

WorkManager был представлен с компонентами Android Jetpack / Architecture.

Решение

  • При первом добавлении зависимости для WorkManager вы можете найти последние версии здесь .
  • Создать Worker class и код для отображения уведомления вdoWork().

  • Запланируйте эту работу при запуске приложения, а также проверьте, не запланировано ли уже.Я уже создал метод с именем isWorkScheduled() в нижнем классе для простоты.

  • Вы можете отправить дополнительные данные для вашей задачи (например, putExtra ()) с помощью setInputData () .

  • Запланируйте одноразовое задание в первом действии onCreate () или Класс приложения onCreate.

Пример

public static final String TAG_WORK = "myTag";
if(!MyWork.isWorkScheduled(TAG_WORK))
    MyWork.scheduleOneTimeTask(TAG_WORK, 5, TimeUnit.DAYS)

MyWork.java

public class MyWork extends Worker {
    public static void scheduleOneTimeTask(String tag, long duration, TimeUnit timeUnit) {
        OneTimeWorkRequest compressionWork =
                new OneTimeWorkRequest.Builder(MyWork.class).setInitialDelay(duration, timeUnit).addTag(tag)
                        .build();
        WorkManager instance = WorkManager.getInstance();
        if (instance != null) {
            instance.enqueue(compressionWork);
        }
    }

    private boolean isWorkScheduled(String tag) {
        WorkManager instance = WorkManager.getInstance();
        if (instance == null) return false;
        LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);
        if (statuses.getValue() == null) return false;
        boolean running = false;
        for (WorkStatus workStatus : statuses.getValue()) {
            running = workStatus.getState() == State.RUNNING | workStatus.getState() == State.ENQUEUED;
        }
        return running;
    }

    @NonNull
    @Override
    public Result doWork() {
        // show notification
        return Result.SUCCESS;
        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

Я предложил вам только WorkManger, потому что я создал образец более ранних дней с JobScheduler , EvernoteJobs , AlarmManager , JobService и WorkManager .В котором я начал периодическое задание по 15 минут с каждого из них.и записывал журналы каждого в отдельном файле при вызове.

Заключение этого теста было таковым. WorkManager и EvernoteJobs были наиболее эффективными для выполнения заданий.Теперь, потому что EvernoteJobs будет использовать WorkManager из следующей версии.Поэтому я разработал WorkManager.

Обновление

Минимальное время для планирования периодического задания составляет 15 минут, поэтому имейте в виду.Вы можете прочитать больше в документации.

0 голосов
/ 30 августа 2018

Устройство выключалось когда-либо в течение пятидневного периода? Согласно документации:

По умолчанию все тревоги отменяются при выключении устройства. Чтобы этого не происходило, вы можете настроить приложение так, чтобы оно автоматически перезапускало повторяющийся сигнал тревоги, если пользователь перезагружает устройство. Это гарантирует, что AlarmManager продолжит выполнять свою задачу без необходимости перезапуска тревоги вручную. Установка будильника при перезагрузке устройства.

Еще одна вещь, которая приходит на ум, вы можете использовать AlarmManager.RCT_WAKEUP вместо AlarmManager.RTC, чтобы убедиться, что устройство не спит, чтобы доставить намерение и создать и отправить ваше уведомление.

Попробуйте:

if (Utils.isKitKat()) {
    mAlarmManager.setWindow(AlarmManager.RTC_WAKEUP, fireDate, AlarmManager.INTERVAL_HOUR, mPendingIntent)
} else {
    mAlarmManager.set(AlarmManager.RTC_WAKEUP, fireDate, mPendingIntent)
}
...