通过AlarmManager和BroadcastReceiver发送通知不起作用?

时间:2018-08-25 11:49:32

标签: android android-notifications android-alarms android-broadcastreceiver

我要提前5天安排通知,因此我使用AlarmManager创建了一个警报,该警报会触发PendingIntent来触发我的BroadcastReceiver。

如果我尝试10秒钟的代码,它将起作用。当我尝试5天后,什么也没发生。

NotificationScheduler类是用于设置和更新警报的辅助类。

解雇日期是正确的,因为我将其存储在数据库中,并且我已经对其进行了证明。

清单:

<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 :(得分:2)

我在使用AlarmManager时遇到问题,所以我现在正在使用WorkManager。

WorkManager是由Android Jetpack /体系结构组件引入的。

解决方案

  • 首先为WorkManager添加依赖项,您可以找到最新版本here
  • 创建 Worker class ,并在 doWork() 中显示代码以显示通知。

  • 在您的应用开始时安排这项工作,还请检查是否尚未安排。为了方便起见,我已经在下面的类中创建了一个名为isWorkScheduled()的方法。

  • 您可以通过setInputData()向任务发送其他数据(例如putExtra())。

  • 在第一次活动onCreate()或Application类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分钟,因此请记住。您可以在documentation.

中阅读更多内容

答案 1 :(得分:0)

在五天内是否关闭了设备?根据文档:

  

默认情况下,设备关闭时所有警报将被取消。为防止这种情况发生,您可以将应用程序设计为在用户重新启动设备时自动重新启动重复警报。这样可以确保AlarmManager将继续执行其任务,而无需用户手动重新启动警报。   Set an alarm when the device restarts.

想到的另一件事是,您可能希望使用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)
}

答案 2 :(得分:0)

最有可能的情况是,您的设备进入打ze模式并且警报管理器未触发您计划的警报 尝试使用(这是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