Android AlarmManager: how to avoid to going off of

2019-04-10 02:26发布

问题:

Hi and thank you for your help.

I have an application that uses AlarmManager to set one alarm a day for the next weeks, months...

  • I set one alarm for each day of the week to start the Activity

and

  • one alarm for each day of the week for stopping the Activity after some time

I have the following problem that I will try to explain in the following lines:

Today is Wednesday,

I open the application and set my alarms for MON, TUE, WED, THU, FRI, SAT, SUN... as soon as I set the alarms:

ALL the alarms for MON and TUE go immediately off I end up with 4 instances of the Activity !!!!

Please how do I avoid this???

Please see a piece of my code:

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

This is the 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();
  }
}


}

回答1:

Do we have a solution here ? I am still looking for solution, my code as below..

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


回答2:

As the API page of AlarmManager states regarding the set(...) and setRepeating(...) methods:

"If the stated trigger time is in the past, the alarm will be triggered immediately, with an alarm count depending on how far in the past the trigger time is relative to the repeat interval."

So, if for example you are on Wednesday, then the AlarmManager will fire the events of: Sunday, Monday, Tuesday and (possibly) Wednesday. It will actually fire only one for all of them.

Here is how I avoid this. Not sure it is the best way, but it works smoothly.

Let's say that you store the day of the week, the hour and the minute of the day in which you want to fire your AlarmManager in the following variables:

final int myAlarmDayOfTheWeek = ...;
final int myAlarmHour = ...;
final int myAlarmMinute = ...;

Of course, here I'm assuming you store the day of the week with the same convention of the Calendar class, i.e.:

Sunday = Calendar.SUNDAY = 1

Monday = Calendar.MONDAY = 2

... ... ...

Saturday = Calendar.SATURDAY = 7

Then:

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

Note that by using Calendar.DAY_OF_YEAR in the Calendar.add(...), you don't even need to take care of the change of the month...

If you are in the first condition (i.e. myAlarmDayOfTheWeek > timestamp.get(Calendar.DAY_OF_WEEK)), you can also just use Calendar.set(...) with Calendar.DAY_OF_WEEK as field and myAlarmDayOfTheWeek as value.

Now, you just need to setup your AlarmManager. For example:

final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, timestamp.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, myAlarmPendingIntent);

Please, also avoid calculating the day interval (i.e. 24 * 60 * 60 * 1000) every time, but use AlarmManager.INTERVAL_DAY (or at most 86400000) instead.



回答3:

From AlarmManager

int RTC_WAKE Alarm time in System.currentTimeMillis() (wall clock time in UTC), which will wake up the device when it goes off.`

From Calendar

Calendar.getInstance()

a Calendar subclass instance set to the current date and time in the default Timezone.

Those two use will different time zones.

Also by changing only DAY_OF_WEEK you are not changing WEEK_OF_YEAR field of a Calendar object. Meaning that if it is Wed but you are setting up an alarm for Mon (by changing only day of week) it will go off immediately as it is set for the Mon of this week. If you need to set the alarms for every day of the week at the same hour

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

This will set up the very same alarm, repeating every 24h. If you need to do different things on different days just use

switch(calendarObj.get(Calendar.DAY_OF_WEEK) { }


回答4:

Just register today's date in shared preference with boolean and make it true when it will be executed for the first time. During restart if the notification is already delivered then ignore else notify the user. On next day just delete old preference and create new sharedpreference value

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

}