添加多个提醒会导致它们同时触发

Adding multiple reminders causes them to trigger at the same time

我在这里包含了完整的问题描述,因为我不确定解决方案背后的逻辑是否正确,但我很确定这与我自己设置警报的方式有关导致这种不准确,或者有时只是纯粹的错误(警报根本不会触发)。


用户可以从药物列表中添加新药物。

屏幕 1

当找到某种药物时,点击它会出现这个画面http://imgur.com/nLC9gTG

该屏幕包含药物的名称,在 "Posology" 标题(绿色条)下可以添加该药物的提醒。

忘记 "Units" 字段。

"Frequency" 字段接受一个数字并且 "Frequency" 字段右侧的标签是可点击的,它会导致出现一个下拉菜单,用户可以从中 select "times for day" 或 "times per week".

"Days of week" 标签(屏幕截图中的标签为空)也是可点击的,它会向用户显示一个下拉菜单,用户可以从该菜单中 select 从某天开始的多天周.

"Treatment Duration" 字段接受数字,"Treatment Duration" 字段右侧的标签将反映用户对 "Frequency" 的选择(如果是 "times per week",则标签会显示 "weeks",如果它是 "times per month",那么该标签会显示 "months")。


屏幕 2

在第二个屏幕截图中 http://imgur.com/AcUmlHH -- 有一个开关允许用户为他尝试添加的药物(项目、实例等)启用提醒。

如果上面的 "Frequency" 字段的数字大于 0(例如 2),则提醒 Switch 将创建一个提醒字段列表,它将显示在 "Get Notifications" 下方绿条。

当用户最终按下 "Add Medication" 时,将在数据库中创建一个新的药物 object,以及用户选择的 "Frequency"(提醒次数)添加此药物 object.


Create a Medication table:

id
name
description
dosage
frequency
frequencyType
treatmentDuration
ForeignCollection<MedicationReminder>
ArrayList<DayChoice> (DayChoice is a class with "Day Name" and "Selected")
when
whenString
units
unitForm
remarks
remindersEnabled

Create a MedicationReminder table:

Medication (foreign key for the Medication table)
Calendar
int[] days_of_week
totalTimesToTrigger

Upon creating this new Medication object:

Medication medication = new Medication();
medication.setFrequency()
medication.setName().setDosage().setRemindersEnabled()....

assignForeignCollectionToParentObject(medication);

assignForeignCollectionToParentObject(Medication)

private void assignForeignCollectionToParentObject(Medication medicationObject) {
    medicationDAO.assignEmptyForeignCollection(medicationObject, "medicationReminders");

    MedicationRemindersRecyclerAdapter adapter =
        (MedicationRemindersRecyclerAdapter) remindersRecyclerView.getAdapter();

    //Clear previous reminders
    medicationObject.getMedicationReminders().clear();

    for (int i = 0; i < adapter.getItemCount(); i++) {
      int realDaysSelected = 0;

      MedicationReminder medReminder = adapter.getItem(i);
      medReminder.setMedication(medicationObject);
      medReminder.setDays_of_week(daysOfWeekArray);

      //These days are populated when the user selected them from the "Days of Week" clickable label
      for (int aDaysOfWeekArray : daysOfWeekArray) {
        if (aDaysOfWeekArray != 0) realDaysSelected++;
      }

      medReminder.setTotalTimesToTrigger(
          Integer.parseInt(treatmentDurationET.getText().toString()) * realDaysSelected);
      medicationObject.getMedicationReminders().add(medReminder);
    }

    setupMedicationReminders(medicationObject.getMedicationReminders().iterator());
}

setupMedicationReminders()

public void setupMedicationReminders(Iterator<MedicationReminder> medicationRemindersIterator) {
    PendingIntent pendingIntent;
    AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

    while (medicationRemindersIterator.hasNext()) {
      MedicationReminder medReminder = medicationRemindersIterator.next();

      for (int i = 0; i < medReminder.getDays_of_week().length; i++) {

        int dayChosen = medReminder.getDays_of_week()[i];

        if (dayChosen != 0) {
          medReminder.getAlarmTime().setTimeInMillis(System.currentTimeMillis());
          medReminder.getAlarmTime().set(Calendar.DAY_OF_WEEK, dayChosen);

          Intent intent = new Intent(AddExistingMedicationActivity.this, AlarmReceiver.class);
          intent.putExtra(Constants.EXTRAS_ALARM_TYPE, "medications");
          intent.putExtra(Constants.EXTRAS_MEDICATION_REMINDER_ITEM, (Parcelable) medReminder);

          pendingIntent = PendingIntent.getBroadcast(this, medReminder.getId(), intent,
              PendingIntent.FLAG_UPDATE_CURRENT);

          int ALARM_TYPE = AlarmManager.ELAPSED_REALTIME_WAKEUP;

          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            am.setExactAndAllowWhileIdle(ALARM_TYPE, medReminder.getAlarmTime().getTimeInMillis(),
                pendingIntent);
          } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            am.setExact(ALARM_TYPE, medReminder.getAlarmTime().getTimeInMillis(), pendingIntent);
          } else {
            am.set(ALARM_TYPE, medReminder.getAlarmTime().getTimeInMillis(), pendingIntent);
          }
        }
      }
    }
}

问题是添加用药提示时,总是在添加后不久触发,而且是同时触发。

说我 select 周六和周五的频率 2,治疗持续时间为 1 周。这意味着总共将添加 4 个提醒,周五 2 个,周六 2 个。

当我这样做时,恰好是星期六,警报同时触发,星期六。

怎么了?

The problem is when the medication reminders are added, they are always triggered shortly after being added, and all at the same time.

那是因为这就是您的要求。您一直在读取当前时间并将其设置为提醒时间:

medReminder.getAlarmTime().setTimeInMillis(System.currentTimeMillis());

您从未阅读 this screen 中用户提供的时间,即您的提醒的 Calendar 字段从未设置。这是您的代码:

  MedicationReminder medReminder = adapter.getItem(i);
  medReminder.setMedication(medicationObject);
  medReminder.setDays_of_week(daysOfWeekArray);

  for (int aDaysOfWeekArray : daysOfWeekArray) {
    if (aDaysOfWeekArray != 0) realDaysSelected++;
  }

  medReminder.setTotalTimesToTrigger(...);

您错过了实际设置提醒时间的线路

当你这样做时:

medReminder.getAlarmTime().setTimeInMillis(System.currentTimeMillis());
medReminder.getAlarmTime().set(Calendar.DAY_OF_WEEK, dayChosen);

结果不可预测。如果今天是星期一,并且您用 Calendar.THURSDAY 调用 set(Calendar.DAY_OF_WEEK),是否应该将日期更改为上一个星期四?还是下周四?你不知道。

如果您的闹钟立即全部响起,这表明更改 DAY_OF_WEEK 会导致日历倒退而不是前进。为验证这一点,在设置 DAY_OF_WEEK 后,调用 getTimeInMillis() 并将其与当前时间进行比较。如果它更小,那么你的日历已经回到过去了。要解决此问题,只需将日历添加 7 天。


此外,您正在使用这种类型的闹钟:AlarmManager.ELAPSED_REALTIME_WAKEUP。此类型采用表示自设备启动以来经过的时间量的值。

但是,您正在使用 RTC 作为时间值(即:Calendar.getTimeInMillis())。这 2 个值是不兼容的。如果要使用RTC,需要使用AlarmManager.RTC_WAKEUP.

这可能不是您问题的答案,但恕我直言,您应该考虑一下。

我认为这是糟糕的用户体验。此应用程序的用户可能会对仅基于时间的提醒时间感到困惑(没有日期)。因此,最好在用户可以编辑的每个提醒旁边添加完整日期。第一次生成它们时,请按正确的顺序设置它们。

Reminder 1:  2/2/2017 13:04

Reminder 2:  9/2/2017 13:04

Reminder 3: 16/2/2017 13:04

Reminder 4: 25/2/2017 13:04

这也会更好地反映 real-world。让我们考虑这个用例:用户带着他的 phone,但他忘记了吃药。他不能在正确的时间接受它,所以他会在某个时候(甚至明天)接受它。这会打乱计划,但在这种情况下,他可以将日期编辑为他实际服药的日期。然后,您在他编辑的提醒之后调整所有提醒的日期和时间,以遵循基于 times_per_week、times_per_month 的间隔。他应该无法将它们更改为过去的日期。

Reminder 1:  2/2/2017 13:04 // He took medication on time

Reminder 2:  9/2/2017 13:04 // He missed this one and changed to date bellow
            10/2/2017 12:22 // This is when he actually took the medication

Reminder 3: 16/2/2017 13:04 // This is no longer valid
            17/2/2017 12:22  // You adjust reminder to this date and time

Reminder 4: 25/2/2017 13:04 // This is no longer valid
            26/2/2017 12:22 // You adjust reminder to this date and time

对于每个提醒,用户仍必须限制在一定的日期范围内。你不能让他随意挑选约会对象。这是基于他的计划设置和当前日期,但我不想谈这个。这是很多工作。

如果将这些更改写入数据库,应该不会丢失数据。您稍后可以向您的应用程序添加一个功能:关于用户迟到次数与用户按时服药次数的报告。这是移动应用程序的一个不错的小功能。