我提前5天安排通知,所以我使用AlarmManager创建一个警报,它触发一个触发我的BroadcastReceiver的PendingIntent。
如果我尝试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()
}
}
}
广播接收器:
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
}
}
我遇到了AlarmManager的问题,所以我现在正在使用WorkManager。
WorkManager与Android Jetpack / Architecture Components一起推出。
Worker class
,以及在doWork()
中显示通知的代码。isWorkScheduled()
的方法。例
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.中阅读更多内容
该设备是否在五天期间关闭?根据文件:
默认情况下,设备关闭时会取消所有警报。为防止这种情况发生,您可以将应用程序设计为在用户重新启动设备时自动重新启动重复警报。这可确保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)
}
最有可能的情况是,当您的设备进入打盹模式并且警报管理器未触发您的预定警报时尝试使用(这是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);
}
}