Android闹钟管理器:如何避免过去的闹钟触发

4

你好,感谢你的帮助。

我有一个应用程序,使用AlarmManager为未来几周、几个月每天设置一个闹钟。

  • 我为每一周的每一天设置了一个启动Activity的闹钟。

还有

  • 我为每一周的每一天设置了一个在一段时间后关闭Activity的闹钟。

我遇到了以下问题,我将在下面的行中尝试解释:

今天是星期三,

我打开应用程序并设置了MON、TUE、WED、THU、FRI、SAT、SUN的闹铃...当我设置了闹铃后:

所有MON和TUE的闹钟立即响起,我最终有4个Activity的实例!

请问如何避免这种情况?

请看我的代码片段:

        // SET THE ALARM FOR STARTING THE ACTIVITY 
        Intent smon = new Intent(ctxt, VideoActivty.class);
        smon.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent psmon = PendingIntent.getActivity(ctxt, 0, smon, 0);

        Calendar calSet = Calendar.getInstance();
        calSet.set(Calendar.DAY_OF_WEEK, 2);
        calSet.set(Calendar.HOUR_OF_DAY, hsmon);
        calSet.set(Calendar.MINUTE, msmon);
        calSet.set(Calendar.SECOND, 0);
        calSet.set(Calendar.MILLISECOND, 0);

        mgr.setRepeating(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(),
                7 * 24 * 60 * 60 * 1000, psmon);

        // SET THE ALARM FOR KILLING THE ACTIVITY 
        Intent fmon = new Intent(ctxt, VideoActivty.class);
        fmon.putExtra("finish", true);
        PendingIntent pfmon = PendingIntent.getActivity(ctxt, 0, fmon, 0);

        calSet.set(Calendar.DAY_OF_WEEK, 2);
        calSet.set(Calendar.HOUR_OF_DAY, hfmon);
        calSet.set(Calendar.MINUTE, mfmon);
        calSet.set(Calendar.SECOND, 0);
        calSet.set(Calendar.MILLISECOND, 0);

        mgr.setRepeating(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(),
                7 * 24 * 60 * 60 * 1000, pfmon);

这是Activity:

public class VideoActivty extends Activity {
private VideoView video;
private MediaController ctlr;
private PowerManager.WakeLock wl;
private KeyguardLock keyguard;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //
    PowerManager pm = (PowerManager) this
            .getSystemService(this.POWER_SERVICE);
    wl = pm.newWakeLock(
            PowerManager.PARTIAL_WAKE_LOCK, "");
    wl.acquire();

    KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
    keyguard = km.newKeyguardLock("MyApp");
    keyguard.disableKeyguard();

    getWindow().setFormat(PixelFormat.TRANSLUCENT);
    VideoView videoHolder = new VideoView(this);
    //if you want the controls to appear
    videoHolder.setMediaController(new MediaController(this));
    Uri video = Uri.parse("android.resource://" + getPackageName() + "/" 
    + R.raw.ingress); //do not add any extension
    //if your file is named sherif.mp4 and placed in /raw
    //use R.raw.sherif
    videoHolder.setVideoURI(video);
    setContentView(videoHolder);

    videoHolder.start();
    videoHolder.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

        @Override
        public void onCompletion(MediaPlayer mp) {

                        mp.start();
        }
    });
}


@Override
protected void onNewIntent (Intent i){


  if( i.getBooleanExtra("finish",false) ){
      wl.release();
      keyguard.reenableKeyguard();
      finish();
  }
}


}
4个回答

5
作为AlarmManager的API页面所述,关于set(...)setRepeating(...)方法:“如果规定的触发时间已经过去,则会立即触发警报,警报计数取决于触发时间相对于重复间隔的时间差。”因此,例如当您处于星期三时,AlarmManager将触发以下事件:星期日、星期一、星期二和(可能)星期三。实际上,它只会为所有事件触发一次。
以下是我如何避免这种情况的方法。不确定它是否是最好的方式,但它可以平稳地工作。
假设您将要在以下变量中存储每周的日期小时分钟,以便在特定时间触发AlarmManager:
final int myAlarmDayOfTheWeek = ...;
final int myAlarmHour = ...;
final int myAlarmMinute = ...;

当然,这里我假设您存储的星期几日历类相同,即:

星期日 = Calendar.SUNDAY = 1

星期一 = Calendar.MONDAY = 2

... ... ...

星期六 = Calendar.SATURDAY = 7

那么:

Calendar timestamp = Calendar.getInstance();

//Check whether the day of the week was earlier in the week:
if( myAlarmDayOfTheWeek > timestamp.get(Calendar.DAY_OF_WEEK) ) {
    //Set the day of the AlarmManager:
    time.add(Calendar.DAY_OF_YEAR, (myAlarmDayOfTheWeek - timestamp.get(Calendar.DAY_OF_WEEK)));
}
else {
    if( myAlarmDayOfTheWeek < timestamp.get(Calendar.DAY_OF_WEEK) ) {
        //Set the day of the AlarmManager:
        timestamp.add(Calendar.DAY_OF_YEAR, (7 - (timestamp.get(Calendar.DAY_OF_WEEK) - myAlarmDayOfTheWeek)));
    }
    else {  // myAlarmDayOfTheWeek == time.get(Calendar.DAY_OF_WEEK)
        //Check whether the time has already gone:
        if ( (myAlarmHour < timestamp.get(Calendar.HOUR_OF_DAY)) || ((myAlarmHour == timestamp.get(Calendar.HOUR_OF_DAY)) && (myAlarmMinute < timestamp.get(Calendar.MINUTE))) ) {
            //Set the day of the AlarmManager:
            timestamp.add(Calendar.DAY_OF_YEAR, 7);
        }
    }
}

//Set the time of the AlarmManager:
timestamp.set(Calendar.HOUR_OF_DAY, myAlarmHour);
timestamp.set(Calendar.MINUTE, myAlarmMinute);
timestamp.set(Calendar.SECOND, 0);

请注意在Calendar.add(...)中使用Calendar.DAY_OF_YEAR,您甚至不需要考虑月份的变化...
如果您处于第一个条件下(即myAlarmDayOfTheWeek > timestamp.get(Calendar.DAY_OF_WEEK)),您也可以使用Calendar.set(...)Calendar.DAY_OF_WEEK为字段,myAlarmDayOfTheWeek为值。
现在,您只需要设置AlarmManager。例如:
final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, timestamp.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, myAlarmPendingIntent);

请避免每次计算天数间隔(即24 * 60 * 60 * 1000),而应使用AlarmManager.INTERVAL_DAY(或至多86400000)来代替。

4
我们有解决方案吗?我还在寻找解决方案,下面是我的代码...
calendar.set(Calendar.DAY_OF_WEEK,getweekday(obj));
    calendar.set(Calendar.HOUR_OF_DAY,timepicker.getCurrentHour());
    calendar.set(Calendar.MINUTE, timepicker.getCurrentMinute());
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    if(calendar.before(cal_now)){//if its in the past increment
        calendar.add(Calendar.DATE,1);
    }

PendingIntent sender = PendingIntent.getBroadcast(getApplicationContext(),_id, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
    am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),AlarmManager.INTERVAL_DAY * 7, sender);

1

来自AlarmManager

int RTC_WAKE Alarm时间以System.currentTimeMillis()(UTC中的壁钟时间)表示,当它响起时将唤醒设备。

来自日历

Calendar.getInstance()

一个Calendar子类实例,设置为默认时区中的当前日期和时间。

这两个使用不同的时区。

同时,仅更改DAY_OF_WEEK不会更改Calendar对象的WEEK_OF_YEAR字段。这意味着如果今天是星期三,但您只更改了星期一的日期,则它将立即响起,因为它已经设置为本周的星期一。如果您需要在每周的相同时间设置闹钟,请设置每天的闹钟。

    alarmManager(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (24hoursOfMilliseconds), 24hoursOfMilliseconds, pendingIntent);

这将设置相同的闹钟,每24小时重复一次。如果您需要在不同的日子里做不同的事情,只需使用。
switch(calendarObj.get(Calendar.DAY_OF_WEEK) { }

谢谢!但我不确定我理解了...请问我该如何修复这个问题?非常感谢。丽莎 - Lisa Anne
UTC就是GMT。如果你在伦敦以西的任何地方,那么你就处于过去时。根据需要将你的Calendar对象的时区更改为UTC。 - Joel Bodega
但是在2013年七月的另一天,抱歉我还是不明白。无论如何谢谢。 - Lisa Anne
我尝试了你建议的方法,但问题并没有解决... 无论如何还是谢谢你! - Lisa Anne
第一个参数是初始启动时间。应该首次启动的时间。通过将其偏移该量(如果我理解正确为一周),您可以获得当前周的日历对象,将星期几更改为所需的星期几,但是直到过去一周后才会执行。顺便提一下,请确保使用日历的静态字段来表示一周内的每一天。它们并不像您认为的那样编号 - Joel Bodega
显示剩余6条评论

0
只需在共享首选项中使用布尔值注册今天的日期,并在第一次执行时将其设置为true。重新启动期间,如果通知已经发送,则忽略,否则通知用户。在下一天,只需删除旧的首选项并创建新的共享首选项值即可。
public boolean isAlreadyWished(String date,String previousDate){
    SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
    if(sharedPref.contains(alreadyNotifiedString+previousDate)){
        sharedPref.edit().remove(alreadyNotifiedString+previousDate);
    }
    if(sharedPref.contains(alreadyNotifiedString+date)){
        boolean displayOrderString = sharedPref.getBoolean(alreadyNotifiedString+date, false);
        return  displayOrderString;
    }else{
        SharedPreferences.Editor editor=sharedPref.edit();
        editor.putBoolean(alreadyNotifiedString+date,true);
        return  false;
    }

}

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