【发布时间】:2023-04-05 19:08:01
【问题描述】:
我正在尝试开发一个简单的提醒应用程序,当用户创建提醒时,我通过“AlarmManager”类设置了一个警报。为了设置警报,我将提醒的标识符放入数据库中的意图中,然后将该意图放入待处理的意图中。当收到警报时,从收到的意图中取出提醒的标识符并在数据库中搜索。
这种方法对我根本不起作用。通常它表现良好,但有时接收到的意图内部的标识符是旧的标识符,所有应用程序都会失败。这是随机发生的,我找不到一致的方式来重现它。由于这是一个随机失败,我完全迷失了这一点。这完全是荒谬的,因为我在附加意图中收到的标识符不是我设置警报的标识符。相反,它是过去触发的旧警报的标识符。
package bembibre.alarmfix.alarms;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import java.util.Calendar;
import bembibre.alarmfix.database.RemindersDbAdapter;
import bembibre.alarmfix.logging.Logger;
import bembibre.alarmfix.utils.GeneralUtils;
/**
* Created by Max Power on 12/08/2017.
*/
/**
* Sets alarms in the operating system for the reminders of this application.
*/
public class ReminderManager {
/**
* This is the key that identifies a metadata item that is attached to the intent of an alarm of
* a reminder for tracking it.
*/
public static final String EXTRA_ALARM_ID = "extra_alarm_id";
private Context mContext;
private AlarmManager mAlarmManager;
public ReminderManager(Context context) {
mContext = context;
mAlarmManager =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
/**
* Part of the code that is responsible for setting an alarm.
*
* @param taskId data base identifier of the reminder.
* @param alarmId number that helps distinguishing each one of the alarms set for a same reminder.
* @param when when.
*/
public void setReminder(long taskId, long alarmId, Calendar when) throws AlarmException {
PendingIntent pi = getReminderPendingIntent(taskId, alarmId, PendingIntent.FLAG_UPDATE_CURRENT);
try {
this.setAlarm(pi, when);
Logger.log("An alarm has been set successfully for the reminder at " + GeneralUtils.format(when) + ". Reminder id: " + taskId);
} catch (Throwable throwable) {
Logger.log("The system doesn't let us to set an alarm for the reminder at " + GeneralUtils.format(when), throwable);
throw new AlarmException();
}
}
/**
* Unsets the alarm that would trigger for the reminder with the given database identifier.
* When calling this method, the reminder could have been erased from the database and it
* wouldn't be a problem. This method is only for unsetting its associated alarm from the
* system.
*
* @param taskId database identifier of the reminder.
* @param alarmId number that helps distinguishing each one of the alarms set for a same reminder.
* @param date date for logging purposes.
*/
public void unsetReminder(long taskId, long alarmId, String date) {
PendingIntent pi = getReminderPendingIntent(taskId, alarmId, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
Logger.log("An alarm has been unset successfully for the reminder at " + date + ". Reminder id: " + taskId);
}
/**
* Returns the <code>PendingIntent</code> object that must be used for calling this application
* when a reminder's alarm triggers.
*
* @param taskId the number that identifies the associated reminder in the database.
* @param alarmId incremental identifier for each alarm of the same reminder.
* @param flag flag that controls the behaviour of the pending intent.
* @return the <code>PendingIntent</code> object.
*/
private PendingIntent getReminderPendingIntent(long taskId, long alarmId, int flag) {
Intent i = new Intent(mContext, OnAlarmReceiver.class);
i.putExtra(RemindersDbAdapter.KEY_ROWID, taskId);
i.putExtra(ReminderManager.EXTRA_ALARM_ID, alarmId);
PendingIntent pi = PendingIntent.getBroadcast(mContext, (int)taskId, i, flag);
return pi;
}
/**
* Sets the alarm in the operating system.
*
* @param operation
* @param when
*/
private void setAlarm(PendingIntent operation, Calendar when) throws Throwable {
/*
* The alarm must be set differently depending on the OS version. Anyway, we need the
* pending intent in order to know what was the reminder for which the alarm was fired, so
* then the correct notification will be shown.
*/
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Before Marshmallow, we can do this for setting a reliable alarm.
mAlarmManager.set(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), operation);
} else {
/*
* Starting from Marshmallow, it seems like this is the only way for setting a reliable
* alarm.
* If we use the "alarm clock" framework, the user will see a icon of an alarm clock.
* If we use the setExactAndAllowWhileIdle the user will see nothing, but the OS can
* delay alarms at some sort of situations.
*/
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), operation);
}
}
}
编辑:当警报触发时,标识符是正确的,但是,当我为打开具有所需标识符的应用程序的通知创建另一个待处理意图时,当问题发生时,它就在使用的待处理意图中当我触摸通知时。这是执行此操作的代码:
private void makeNotification(Long rowId, String title, String body) {
android.app.NotificationManager mgr = (android.app.NotificationManager)context.getSystemService(NOTIFICATION_SERVICE);
Intent notificationIntent;
long notificationId;
if (rowId == null) {
notificationId = 0;
notificationIntent = new Intent(context, ReminderListActivity.class);
} else {
notificationId = rowId;
notificationIntent = new Intent(context, ReminderEditActivity.class);
notificationIntent.putExtra(RemindersDbAdapter.KEY_ROWID, rowId);
}
PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent,
PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setContentTitle(title)
.setContentText(body)
.setContentIntent(pi)
.setStyle(new NotificationCompat.BigTextStyle().bigText(body));;
Notification note = mBuilder.build();
note.defaults |= Notification.DEFAULT_SOUND;
note.flags |= Notification.FLAG_AUTO_CANCEL;
// An issue could occur if user ever enters over 2,147,483,647 tasks. (Max int value).
// I highly doubt this will ever happen. But is good to note.
int id = (int)((long)notificationId);
mgr.notify(id, note);
NotificationManager.setNotified(context, id);
}
编辑:我发现至少有一种情况我总是可以重现失败。步骤:
- 为当前日期创建提醒。
- 等待通知,因为它设置为当前日期,它几乎会立即到来。
- 删除提醒。
- 放弃通知(不要进入其中)。
- 为当前日期创建另一个提醒。
- 等待通知。
- 通过单击进入通知内部:失败 转载。
【问题讨论】:
-
请接受您自己的答案。这将从未回答的问题列表中删除该问题。
标签: android android-intent alarmmanager android-pendingintent