Android Alarm Manager sets repetition at a specific time

I am having a problem with the alarm manager in Android. So what I'm trying to make the alarm repeat, to start the DB insert every day around 12.01AM.

Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 0 ); calendar.set(Calendar.MINUTE, 1); notificationCount = notificationCount + 1; AlarmManager mgr = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); Intent notificationIntent = new Intent(context, ReminderAlarm.class); notificationIntent.putExtra("NotifyCount", notificationCount); PendingIntent pi = PendingIntent.getBroadcast(context, notificationCount, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi); 

So basically I came up with this code. However, the alarm manager starts up again after the minute I installed it.

Let's say I run applications on 10/01/2014 5.48PM. I wanted this to trigger DB insertion when onReceive every day after I set it to only 12.01AM. But somehow, the alarm manager runs on 01/10/2014 5.49PM, which a minute after I installed it, and it stops working.

I wonder what part I did wrong.

Thanks in advance.

EDIT

Repeating class For this class, it starts the alarm manager every day and passes the variables along with a reminder of the alarm class to insert the database.

 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.recurring); context = this; buildListView(); if(!alarmInitialized(this)) { scheduleAlarms(this); } } // And the few methods you suggested to schedule the alarm public static void scheduleAlarms(Context context) { Calendar calendar = Calendar.getInstance(); if (hasRunnedToday(context)) { // if the alarm has run this day calendar.add(Calendar.DATE, 1); // schedule it to run again starting // tomorrow } long firstRunTime = calendar.getTimeInMillis(); AlarmManager mgr = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); Intent notificationIntent = new Intent(context, ReminderAlarm.class); PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent, 0); mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstRunTime, AlarmManager.INTERVAL_DAY, pi); ComponentName receiver = new ComponentName(context, BootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } 

Class BootReceiver

 public void onReceive(Context context, Intent i) { if (i.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { Recurring.scheduleAlarms(context); } } 

ReminderAlarm class Basically for this class, it simply grabs the variable passed from the Recurring class and performs DB insertion. I inserted some Toast.makeText to check if it is being extracted, but no luck testing it.

 public class ReminderAlarm extends BroadcastReceiver { private NotificationManager mNotificationManager; private Notification notification; @Override public void onReceive(Context context, Intent intent) { String recurID = null; String recurStartDate = null; String currentDate = null; String description = null; String type = null; String amount = null; String categoryName = null; String frequencyStr = null; String nextPaymentDate = null; SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); DatabaseAdapter mDbHelper = new DatabaseAdapter(context); mDbHelper.createDatabase(); mDbHelper.open(); RecurringController rc = new RecurringController(mDbHelper.open()); ArrayList<RecurringModel> recur_list = rc.getAllRecurring(); // THIS PART TO GET DATA FROM DATABASE for (int i = 0; i < recur_list.size(); i++) { recurID = recur_list.get(i).getRecurringID(); recurStartDate = recur_list.get(i).getRecurringStartDate(); currentDate = dateFormat.format(new Date()); description = recur_list.get(i).getRecurringDesc(); type = recur_list.get(i).getRecurringType(); amount = Float.toString(recur_list.get(i).getRecurringAmount()); categoryName = recur_list.get(i).getCategoryID(); frequencyStr = recur_list.get(i).getFrequency(); Toast.makeText(context, description, Toast.LENGTH_LONG) .show(); Toast.makeText(context, recurStartDate Toast.LENGTH_LONG) .show(); Calendar cal = Calendar.getInstance(); try { cal.setTime(dateFormat.parse(recurStartDate)); if (frequencyStr.equals("Daily")) { cal.add(Calendar.DAY_OF_MONTH, 1); nextPaymentDate = dateFormat.format(cal.getTimeInMillis()); cal.add(Calendar.DAY_OF_MONTH, -1); } else if (frequencyStr.equals("Weekly")) { cal.add(Calendar.WEEK_OF_YEAR, 1); nextPaymentDate = dateFormat.format(cal.getTimeInMillis()); cal.add(Calendar.WEEK_OF_YEAR, -1); } else if (frequencyStr.equals("Monthly")) { cal.add(Calendar.MONTH, 1); nextPaymentDate = dateFormat.format(cal.getTimeInMillis()); cal.add(Calendar.MONTH, -1); } else if (frequencyStr.equals("Yearly")) { cal.add(Calendar.YEAR, 1); nextPaymentDate = dateFormat.format(cal.getTimeInMillis()); cal.add(Calendar.YEAR, -1); } } catch (ParseException e) { e.printStackTrace(); } // If dates match then execute the SQL statements if (currentDate.equals(nextPaymentDate)) { // mDbHelper.createDatabase(); // mDbHelper.open(); TransactionRecModel trm = new TransactionRecModel(); CategoryController cc = new CategoryController(mDbHelper.open()); trm.setDate(currentDate); trm.setTransDescription(description); trm.setType(type); trm.setAmount(Float.parseFloat(amount)); // Get the categoryID based on categoryName String catID = cc.getCatIDByName(categoryName); trm.setCategory(catID); // Check if the recurring record exists before insert new // transaction record boolean recurExist = rc.checkRecurExist(recurStartDate, description, catID); if (recurExist == true) { TransactionRecController trc = new TransactionRecController( mDbHelper.open()); // Check if the transaction record exists to prevent // duplication boolean moveNext = trc.checkTransExist(trm); if (moveNext == false) { if (trc.addTransactionRec(trm)) { // Update recurring start date after insertion of // transaction RecurringModel rm = new RecurringModel(); rm.setRecurringID(recurID); rm.setRecurringStartDate(currentDate); if (rc.updateRecurringDate(rm)) { mNotificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); PendingIntent contentIntent = PendingIntent .getActivity( context, Integer.parseInt(intent .getExtras() .get("NotifyCount") .toString()), new Intent(), 0); notification = new Notification( R.drawable.ic_launcher, "Notification", System.currentTimeMillis()); notification.setLatestEventInfo(context, description, nextPaymentDate, contentIntent); mNotificationManager .notify(Integer.parseInt(intent .getExtras().get("NotifyCount") .toString()), notification); mDbHelper.close(); } } } } mDbHelper.close(); } } mDbHelper.close(); Recurring.updateAlarmLastRun(context); } } 

I added this part of the codes to the part that you proposed to schedule an alarm to call the BootReceiver class. Then, from the BootReceiver class, I will call back to the Recurring class and Reminder Alarm class:

 ComponentName receiver = new ComponentName(context, BootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 
+1
source share
1 answer

The problem is calendar.getTimeInMillis() in

 mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi); 

The second argument to setInexactRepeating , citing the document

triggerAtMillis times in milliseconds so that the alarm is first turned off using the appropriate clock (depending on the type of alarm). This is inaccurate: the alarm does not work until this time, but it may delay almost the entire alarm interval before the first alarm call.

The value will be executed for the first time aproximally one minute after you set it due

 calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 0 ); calendar.set(Calendar.MINUTE, 1); 

If you are not the first to start an alarm the next day, do calendar.add (Calendar, DATE, 1); `

As for the shutdown, did the device reboot? AlarmCalendar alarms are not saved when the device is rebooted, can you register the BroadcastReceiver to receive the BOOT_COMPLETED event and register the alarm re-check ? BOOT_COMPLETED Alarm manager persist even after the reboot?

Update: as suggested here, it will help you after looking at the code

In class BOOT_COMPLETED Receiver :

 public void onReceive(Context context, Intent i) { if (i.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { ReminderAlarm.scheduleAlarms(this); } } 

In class ReminderAlarm

 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.recurring); if(!alarmInitialized(this) { scheduleAlarms(this); } 

}

 public static void scheduleAlarms(Context context) { Calendar calendar = Calendar.getInstance(); if(hasRunnedToday(context)) { //if the alarm has run this day calendar.add(Calendar.DATE, 1); //schedule it to run again starting tomorrow } long firstRunTime = calendar.getTimeInMillis(); AlarmManager mgr = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); Intent notificationIntent = new Intent(context, ReminderAlarm.class); PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent, 0); mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstRunTime, AlarmManager.INTERVAL_DAY, pi); } public static boolean alarmInitialized(Context context) { SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE); long alarmLastRun = preferences.getLong("AlarmLastRun", -1); return alarmLastRun != -1; } public static void updateAlarmLastRun(Context context) { SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE); preferences.edit() .putLong("AlarmLastRun", new Date().getTime()) .apply(); } public static boolean hasRunnedToday(Context context) { SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE); long alarmLastRun = preferences.getLong("AlarmLastRun", -1); if(alarmLastRun == -1) { return false; } //check by comparing day, month and year Date now = new Date(); Date lastRun = new Date(alarmLastRun); return now.getTime() - lastRun.getTime() < TimeUnit.DAYS.toMillis(1); } 

Each time a call to the Reminder class starts, you must call updateAlarmLastRun to update the last time the alarm was started, this is necessary because the alarm can be a schedule that must be executed during the day, and the user restarts the device before the alarm starts, in in this case we do not want to use calendar.add(Calendar.DATE, 1); since it will skip the day.

On your Manifest.xml

 <receiver android:name=".BootReceiver" android:enabled="true" android:exported="false" android:permission="android.permission.RECEIVE_BOOT_COMPLETED"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> 

Notes:

  • You should not do context = this if the context is a class field, since the object contains a link to the field of its context field and context contains a reference to the object that will flow
  • Your Receiver 'onReceive` has no additional settings that you think you have as a “notificationCount” onReceive when your device finishes booting.
  • Once your alarm starts, call updateAlarmLastRun

Hope this helps

+5
source

Source: https://habr.com/ru/post/1210780/


All Articles