Why AlarmManager fires are inconsistent - java

I am registering an Alarm manager in the onResume() state in my MainActivity.java (which is the main activity where the program start)
protected void onResume() {
super.onResume();
if (Helper.isNetworkAvailable(this)) {
Intent intent = new Intent(this, NewsIntentService.class);
PendingIntent pi = PendingIntent.getService(this, 0, intent, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC, System.currentTimeMillis(),
10 * 60 * 1000, pi);
} else {
// nothing done
}
}
However I was getting inconsistent results, the following code runs good and with no errors, it shows that the PendingIntent should be fired at every 10 minutes but the results where on the following from logcat for example:
It starts working good:
2:00 pm (fired), 2:10 pm (fired), 2:30 pm (fired), ...
But after some time:
3:20 pm (fired), 3:27 pm (fired), 3:33 pm (fired), 3:38 pm (fired) ...
The question is at what life cycle of the activity it is best to register an AlarmManager and if what I did is correct what is the reason for the inconsistent run.

use the following code it worked for me:
1- private SharedPreferences prefs;
2-
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Launch SharedPreferences
prefs = getSharedPreferences("Alarm", MODE_PRIVATE);
3-
protected void onResume() {
super.onResume();
if(!prefs.getBoolean(Helper.ALARM_SET, false)) {
Intent intent = new Intent(this, NewsIntentService.class);
PendingIntent pi = PendingIntent.getService(this, 0, intent, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC, System.currentTimeMillis(),
10 * 60 * 1000, pi);
Log.i(Helper.ALARM_SET, "Alarm is set.");
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Helper.ALARM_SET, true);
editor.commit();
}
}
Explanation:
The Use SharedPreferences to save a boolean value AlARM_SET, simple precise and this code will work even if the phone has restarted or turned off.

Your onResume is getting called at those times, hence triggering the alarm and once again setting it for next ten minutes.
If your end result is to invoke a functionality on Alarm, try to set the next alarm there, call your alarm once in onCreate of your MainActivity.(Check for the first run, use a File for the same or just Shared Preference) Let the rest be handled by the service/function/code which is run upon triggering the alarm.
Check here for complete implementation.
The flow would be something like:
MainActivity--> onCreate--> Check if first run--> Yes--> Register Alarm and execute immediately--> Invoke function/code-->Let this code set the next alarm.

The docs for AlarmManager clearly state that repeating alarms are inexact -- hence the drift you observe via Logcat.
Note: as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.

A block of the code that you can use:
public void schedule(final Context context) {
int alarmCode = YOUR_ALARM_CODE;
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, alarmCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
final Calendar calendar = yourCalendar();
final AlarmManager alarm = (AlarmManager) context.getSystemService(Activity.ALARM_SERVICE);
final long time = calendar.getTimeInMillis();
alarm.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);
}

Related

Reset variable & trigger notification at specific times every day - even when app is closed

So far I've been using AlarmManager and BroadcastReceiver like this:
private void setUpStreakResetAlarm() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.add(Calendar.DAY_OF_MONTH, 1);
Intent intent = new Intent(getApplicationContext(), DailyCounterCheckReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 100, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
And in my DailyCounterCheckReceiver class:
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
int counterOld = sharedPreferences.getInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_OLD_KEY, 0);
int counterToday = sharedPreferences.getInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_KEY, 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
// If user has increased counter on this day, increase the old check for tomorrow
if (counterToday > counterOld) {
counterOld = counterToday;
editor.putInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_OLD_KEY, counterOld);
editor.putBoolean("increasedOld", true);
editor.putBoolean("reset", false);
}
// etc.....
But with new Android versions, background tasks like that just get killed and it's very unreliable.
So what can I use instead? Work Manager, Foreground Service, something else?
And don't I still need AlarmManager to trigger them?
My use case is extremely simple, so I don't think I need some super complex solution, but there are so many options out there. What is better for my simple case?
Edit:
Angel's comment would solve my reset problem, but I also do the same for triggering notifications at certain times:
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("lastTimeOfNotifTrigger", System.currentTimeMillis());
editor.apply();
repository = new NotificationRepository(context);
repository.startGetNextNotificationAsync(this);
}
How could I solve that in the easiest way?
You could use alarmManager.setExact to schedule the alarms (so that your receiver is called exactly at the time you want even if the phone is sleeping or in Doze mode). Once your receiver is called you can schedule the next. Remember to also add receivers detecting BOOT and PACKAGE_CHANGED so that you can re-schedule the alarms in case the phone is rebooted or the app updated.
You could also use Work Manager as you said to schedule a job, if you don't need it to be run at an exact time.
Your code shouldn't have any other issue running in newer versions of Android if you are not trying to launch an Activity from that receiver (you can't launch an activity anymore from the background).

AlarmManager repeats call even if its not time

I'm using alarm manager to call for api. it is called in a activity onCreate. I want it to call an alarm at start of the app then alarms every three hours.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY,1);
AlarmManager alarmManager1 = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent myIntent1 = new Intent(LobbyActivity.this,WeatherBroadCastReceiverCurrent.class);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this,0,myIntent1,0);
alarmManager1.setInexactRepeating(AlarmManager.RTC,calendar.getTimeInMillis(),AlarmManager.INTERVAL_HOUR+
AlarmManager.INTERVAL_HOUR+AlarmManager.INTERVAL_HOUR/*(1000*60*60*3)*/,pendingIntent1);
That activity is then finished and proceeds to another activity when a button is clicked. My problem is-if the activity is recreated it calls an alarm even if it is not the time. Can I set an alarm on a non activity class so it will not be recalled when the activity is recreated?? if so how? Tia
try to run by removing calendar.set(Calendar.HOUR_OF_DAY,1); and run, it will call alarm at start of app
To simply get over this, you need to create a flag and make it true so that the activity can check if the Alarm has been set before, it is has, then it will move forward without setting it.
Use of SharedPreferences is ideal for this.
This is one of my snippets, edit it according to your need.
SharedPreferences prefs;
SharedPreferences.Editor ed;
prefs = PreferenceManager.getDefaultSharedPreferences(this);
ed = prefs.edit();
boolean isOpeningForTheFirstTime = prefs.getBoolean("firstTime", true);
if(!isOpeningForTheFirstTime) {
Intent i = new Intent(this, StartScreen.class);
startActivity(i);
finish();
}
And the AlarmManager can be simplified by removing a few things.
public void setAlarm(){
//To get the current time
long alertTime = new GregorianCalendar().getTimeInMillis();
//Interval of a minute
int timeInterval = 60000;
//Intent which you want to start
Intent alertIntent = new Intent(this, ClassName.class);
//Declaring the alarmManager
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//Setting the alarmmanager up.
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, alertTime, timeInterval, PendingIntent.getBroadcast(this, 1, alertIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}

Resetting data everyday on android app as an asyn task

I have an app that needs to reset its text fields and other views everyday. Its like a daily tasks that remain constant but the progress has to be reset everyday or at 12am
That a really easy task to implement using AlarmManager.
Following this guide it should be easy for you.
Example:
//define your intent
AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
PedingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// Set the alarm to start at approximately 00:00 h(24h format).
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 00);
//repeteat alarm every 24hours
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, alarmIntent);
Now create a BroadcastReceiver called "AlarmReceiver" and inside it, do whatever you want to.
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
//do whatever you want.
}
}
Maybe you should use a WakefulBroadcastReceiver depending of your task.

Alarm Manager does not execute broadcaster class

I'm trying out alarms and have hit a wall. I don't think my alarm is setting up properly because I never get a confirmation after the alarm is supposed to go off. Here's how I call on the alarm manager:
long alarmtime=new GregorianCalendar().getTimeInMillis()+10*1000;//run after 10 seconds
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager alarmman = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmman.cancel(pi); // cancel any existing alarms
alarmman.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
alarmtime, 10 * 1000, pi);//run every 10 seconds
And here's my AlarmReceiver.java:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "TEST", Toast.LENGTH_LONG).show();
}
}
However, the TEST text does not appear, and I can't figure out why.
Since you are using the AlarmManager.ELAPSED_REALTIME_WAKEUP argument, your initial alarm-time should be base on the elapsed real time of the device:
long alarmtime = SystemClock.elapsedRealtime() + 10 * 1000;
alarmman.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmtime, 10 * 1000, pi);
(see this link).
For a BroadcastReceiver it's probably PendingIntent.getBroadcast() instead of PendingIntent.getService(). You are also cancelling the alarm, just update your PendingIntent like this and try not cancelling before:
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
Read the documentation thoroughly for further information.
Make sure that the BroadcastReceiver is being called by doing a System.out.println("TEST"); instead of Toast. If you are able to see that in your logcat, then the problem probably is that you need to run the Toast in UI thread.

preventing android daily alarm from running the intent during alarm setup

I followed the instructions in this thread to create a daily alarm that starts a service at 12:30 each day which shows a notification. The problem I'm having is that setting the alarm also triggers the service (=> and the notification) every time the app starts.
Since I figured that the alarm will run only at the specified time (12:30) then I have no problem setting it when the app starts.
I realize that setting the alarm from scratch every time the app is launched is a bit ineffective since it only needs to be set once (I made it set on device boot as well), but it seemed like the easiest way.
So what's the best way to fix this? is there a way to set the alarm for the specified time without running the intent when setting?
Here's the code if you are interested (this function is called every time when launching the app):
public static void setAlarm(Context context)
{
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction("com.Rani.app.SET_NOTIFICATION_ALARM");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Calendar dailyCheckTime = Calendar.getInstance();
dailyCheckTime.setTimeZone(TimeZone.getTimeZone("GMT"));
dailyCheckTime.set(Calendar.HOUR_OF_DAY, 12);
dailyCheckTime.set(Calendar.MINUTE, 30);
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pendingIntent);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, dailyCheckTime.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
}
thanks in advance.
After trying several things, I used the information in this thread to preform a check if an alarm already exists, and setting one only if there isn't one already.
code:
public static void setAlarm(Context context)
{
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction("com.Rani.app.SET_NOTIFICATION_ALARM");
boolean alarmUp = (PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_NO_CREATE) != null);
// check if an alarm already exists
if (alarmUp == false)
{
// set an alarm in case there isnt one already set
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Calendar dailyCheckTime = Calendar.getInstance();
dailyCheckTime.set(Calendar.HOUR_OF_DAY, 12);
dailyCheckTime.set(Calendar.MINUTE, 30);
if (dailyCheckTime.getTimeInMillis() < Calendar.getInstance()
.getTimeInMillis()) {
dailyCheckTime.set(Calendar.DATE,
dailyCheckTime.get(Calendar.DATE) + 1);
}
AlarmManager alarm = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pendingIntent);
alarm.setRepeating(AlarmManager.RTC_WAKEUP,
dailyCheckTime.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
}
}

Categories