闹钟管理器 - 安排多个不重复事件的调度

25
在Android的Alarm Manager中,如果要安排多个不重复且没有固定间隔重复的闹钟,我们该如何做呢?由于这些闹钟没有任何重复模式,因此无法使用“setRepeating”函数。
我将闹钟时间存储在SQLite数据库表中,活动应该从这个表中获取日期和时间并设置闹钟。
如果我们在循环中设置不同的闹钟,那么它只保留最后一个。我从这篇帖子中读到:如何创建多个闹钟? 它告诉我们将唯一ID附加到意图中,然后设置单独的闹钟事件。但是对我来说没有起作用。
在清单文件中需要添加什么内容以处理这个唯一ID吗?
下面是“RegularSchedule”活动中的代码,它只创建一个闹钟事件:
        while (notifCursor.moveToNext()) {
            Intent intent = new Intent(RegularSchedule.this,
                    RepeatingAlarm.class);

            // The cursor returns first column as unique ID             
            intent.setData(Uri.parse("timer:" + notifCursor.getInt(0)));

            PendingIntent sender = PendingIntent.getBroadcast(
                    RegularSchedule.this, 0, intent, 0);

            // Setting time in milliseconds taken from database table 
            cal.setTimeInMillis(notifCursor.getLong(1));

            AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
            am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
        }

如需进一步的细节或代码片段,请告知。

清单文件(此处RepeatingAlarm扩展了BroadcastReceiver):

    <receiver android:name=".user_alerts.RepeatingAlarm" android:process=":remote" />

    <activity android:name=".user_alerts.RegularSchedule"
        android:label="@string/reg_schedule_title" android:theme="@android:style/Theme.Light">
    </activity>

重复闹钟:

public class RepeatingAlarm extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
.......
    // The PendingIntent to launch our activity if the user selects this notification
    Intent notificationIntent = new Intent (context, DisplayReminder.class);
    PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

    // Set the info for the views that show in the notification panel.
    notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
    notification.defaults |= Notification.DEFAULT_SOUND;
    notification.defaults |= Notification.DEFAULT_VIBRATE;
    notification.defaults |= Notification.DEFAULT_LIGHTS;

    mNotificationManager.notify(2425, notification);
2个回答

29

这是对我有效的解决方案。我分享这个解决方案是为了让其他人受益并快速解决这个问题。

欢迎任何其他意见来更深入地了解解决方案的技术性质并说明为什么某些事情起作用而其他事情则不起作用 :)

(1) 首先,要确保Manifest文件中有接收器(receiver),以便于你的类具有BroadcastReceiver。

    <receiver android:name=".RepeatingAlarm" android:process=":remote">
        <intent-filter>
            <data android:scheme="timer:" />
        </intent-filter>
    </receiver>
请注意该类是主包的一部分。如果它在某个子包中,请将其移到主包中。主包是您在“清单”标记中定义的内容。
'intent-filter' 用于定义 'action' 和 'data'。您可以在这里放置要从待处理意图中调用的 Activity 类。但我发现,如果您在清单中定义 'action',它不会在活动中显示动态值。它只显示静态值。非常奇怪。如果您遇到相同的问题,请勿将 'action' 放入清单中,而应将其作为待处理意图的一部分放入 BroadcastReceiver 类中。
'data' 标记表示您将在使用 AlarmManager 调度不同警报时放置唯一意图的动态 URI。请参考下一步了解更多详情。
(2) 您将使用 AlarmManager 调度警报的 Activity 类: 我正在使用数据库存储我的闹钟时间值,然后使用那些值进行调度。我的光标从表中获取唯一的 _ID 和闹钟时间(自1970年1月1日以来的秒数)。请注意,在此处放置的 URI 与您在清单文件中的 URI 相同。
    Calendar cal = Calendar.getInstance();
    int notifIterator = 0;

    if (notifCursor.getCount() > 0) {
        while (notifCursor.moveToNext()) {
            Intent intent = new Intent(MySchedule.this,
                    RepeatingAlarm.class);

            // As the same intent cancels the previously set alarm having
            // same intent
            // changing the intent for every alarm event so that every alarm
            // gets
            // scheduled properly.
            intent.setData(Uri.parse("timer:" + notifCursor.getInt(0)));

            PendingIntent sender = PendingIntent.getBroadcast(
                    MySchedule.this, 0, intent,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION);

            cal.setTimeInMillis(notifCursor.getLong(1) * 1000);

            AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
            am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);

            notifIterator++;

            Toast mToast = Toast.makeText(
                    RegularSchedule.this,
                    "Reminders added to the calendar successfully for "
                            + android.text.format.DateFormat.format(
                                    "MM/dd/yy h:mmaa",
                                    cal.getTimeInMillis()),
                    Toast.LENGTH_LONG);
            mToast.show();
        }
    }

如果你按照这样做了还是看不到警报,请检查模拟器使用的时区。有时我们安排本地时区,但模拟器会安排GMT时区。如果你看toast消息,那会帮助你找出这个问题。

(3) 最后一个是BroadcastReceiver类。请注意,要打开数据库,你需要使用'context':

public void onReceive(Context context, Intent intent) {

    // Update the status in the notification database table
    int notificationId = Integer.parseInt(intent.getData().getSchemeSpecificPart());

    db = context.openOrCreateDatabase(DATABASE_NAME,
            SQLiteDatabase.CREATE_IF_NECESSARY, null);

    <<<< Do DB stuff like fetching or updating something>>>>

    // Raise the notification so that user can check the details
    NotificationManager mNotificationManager = (NotificationManager) context
            .getSystemService(Context.NOTIFICATION_SERVICE);

    int icon = R.drawable.icon;
    CharSequence tickerText = "your text";
    long when = System.currentTimeMillis();

    Notification notification = new Notification(icon, tickerText, when);

    // Count of number of notifications
    notification.number = notifCount;

    CharSequence contentTitle = "your title ";
    CharSequence contentText = "your notification text";

    // The PendingIntent to launch our activity if the user selects this
    // notification
    Intent notificationIntent = new Intent(context, DisplayReminder.class);
    PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
            notificationIntent, 0);


    // Set the info for the views that show in the notification panel.
    notification.setLatestEventInfo(context, contentTitle, contentText,
            contentIntent);
    notification.defaults |= Notification.DEFAULT_SOUND;
    notification.defaults |= Notification.DEFAULT_VIBRATE;
    notification.defaults |= Notification.DEFAULT_LIGHTS;

    // Instead of 1234 or any other number, use below expression to have unique notifications
    // Integer.parseInt(intent.getData().getSchemeSpecificPart())
    mNotificationManager.notify(1234, notification);
}
请注意,如果您想创建单独的通知,可以在调用notify()时传递请求id作为唯一标识符。最后,您可以创建一个DisplayReminder类,在用户点击通知时调用它。

13
对我来说关键的一行是:intent.setData(Uri.parse("timer:" + notifCursor.getInt(0))); 它确保意图是唯一的,而后续对AlarmManager.set的调用不会取消早期的调用。 - Jonathon Horsman
谢谢 @JonathonHorsman 我创建了这个小类,可以将唯一的新值传递给我,线程安全并将它们保存在共享首选项中(即使应用程序被杀死也可以恢复). - volkersfreunde

1
如@Jonathon Horsman所建议的,确保您创建的意图是唯一的。
如果您想设置10个闹钟:
for(int i=; i<10; i++) {
   Intent intent = new Intent(YourActivity.this,
                YourAlarm.class);
   intent.setData(Uri.parse("timer:" + i);
   PendingIntent sender = PendingIntent.getBroadcast(
                YourActivity.this, 0, intent,
                Intent.FLAG_GRANT_READ_URI_PERMISSION);
   AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
   am.set(AlarmManager.RTC_WAKEUP, yourTimeInMillis, sender);
}

对我来说运行良好。


我在使用Intent.FLAG_GRANT_READ_URI_PERMISSION时遇到了错误 - 类型不正确。我知道我需要设置该标志,以便我可以在onReceive中使用intent.getData().getSchemeSpecificPart()。 - marienke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接