【问题标题】:Adding multiple reminders causes them to trigger at the same time添加多个提醒会导致它们同时触发
【发布时间】:2017-01-23 13:03:55
【问题描述】:

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


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

屏幕 1

当找到某种药物时,点击它会显示这个屏幕http://imgur.com/nLC9gTG

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

忘记“单位”字段。

“频率”字段接受一个数字,并且“频率”字段右侧的标签是可点击的,它会导致出现一个下拉菜单,用户可以从中选择“一天的次数”或“每周的次数” ”。

“星期几”标签(屏幕截图中的标签为空)也是可点击的,它为用户提供了一个下拉菜单,用户可以从中选择一周中的几天。

“治疗持续时间”字段接受一个数字,“治疗持续时间”字段右侧的标签将反映用户选择的“频率”(如果是“每周次数”,则该标签将显示“周” ,如果是“每月次数”,那么该标签将显示“月”)。


屏幕 2

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

如果上面的“频率”字段的数字大于 0(例如 2),则提醒 Switch 将创建一个提醒字段列表,它将显示在“获取通知”绿色条的下方。

当用户最终按下“添加药物”时,将在数据库中创建一个新的药物对象,以及用户选择为此药物对象添加的“频率”(提醒次数)。


创建一个药物表:

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

创建一个 MedicationReminder 表:

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

在创建这个新的 Medication 对象时:

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

assignForeignCollectionToParentObject(medication);

assignForeignCollectionToParentObject(药物)

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);
          }
        }
      }
    }
}

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

假设我为周六和周五选择频率 2,治疗持续时间为 1 周。这意味着总共将添加 4 条提醒,周五 2 条,周六 2 条。

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

怎么了?

【问题讨论】:

    标签: android calendar alarmmanager alarms


    【解决方案1】:

    当你这样做时:

    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())。这两个值是不兼容的。如果要使用RTC,需要使用AlarmManager.RTC_WAKEUP

    【讨论】:

    • 您对 setTimeInMillis(System.currentTimeInMillis) 行是正确的,但是当我去检查它时,似乎我在调试时早就将其删除了,是的,这样做肯定是错误的覆盖任何先前设置的时间,赏金将在 40 分钟后到期,我现在检查并返回此
    • 您也说得对,指定任意 DAY_OF_WEEK 会导致日历(在我检查此日历的日期之前和之后)实际上及时返回。现在我不打算采用加 7 的方法,但我会完全避免使用日历并使用 JodaTime 的 LocalDateTime,这样我就不会遇到这种差异,你怎么看?
    • 使用 JodaTime 有什么好处?你将如何设置星期几?您的代码没有任何问题,您只需要考虑日历将被设置回来而不是向前的可能性。使用 JodaTime 将如何解决这个问题?
    • 我明白了,我认为用 JodaTime 设置日期不会导致这样的不幸,因为我不知道是什么导致它开始,任意设置方式?我将如何做到这一点,因为它不会是一周中的任意一天并避免它回到过去?
    • 如果您想设置一周中的特定日期,使用 JodaTime 会遇到完全相同的问题。你为什么不直接使用我的建议?否则,您可以获取当前时间,获取星期几,确定星期几与您要设置闹钟的星期几之间的天数,然后将这些天数添加到日历中。这也有效,但代码更多。我最初的建议更高效,使用更少的代码。
    【解决方案2】:

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

    那是因为那是你所要求的。您始终在读取当前时间并将其设置为提醒时间:

    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(...);
    

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

    【讨论】:

    • 我实际上是在为警报行提供服务的适配器中添加时间,当单击每一行时,它会提示输入时间选择器,并从该选择器中设置时间和分钟等.. 到 drug.setAlarmTime(),然后从 Activity 回来,我抓住这些对象并使用它们。
    【解决方案3】:

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

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

    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
    

    这也将更好地反映现实世界。让我们考虑这个用例:用户有他的手机,但他忘了吃药。他不能在正确的时间接受它,所以他稍后再接受它(甚至可能是明天)。这会打乱计划,但在这种情况下,他可以将日期编辑为他实际服药的日期。然后,在他编辑的提醒之后调整所有提醒的日期和时间,以遵循基于 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
    

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

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

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-24
      • 1970-01-01
      • 2014-10-17
      • 1970-01-01
      • 2015-01-08
      相关资源
      最近更新 更多