Android repeat alarm manager in not triggering immediately - android

My code:
Calendar calSet = Calendar.getInstance();
calSet.set(Calendar.HOUR_OF_DAY, 11);
calSet.set(Calendar.MINUTE, 20);
calSet.set(Calendar.SECOND, 0);
calSet.set(Calendar.MILLISECOND, 0);
PendingIntent pi=PendingIntent.getBroadcast(context,0,myIntent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP,calSet.getTimeInMillis(),pi);
and say, I'm executing at 11:30. Alarm triggers immediately (which is expected).
But, for the same when I use
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calSet.getTimeInMillis(),24*60*60*1000,pi);
alarm is not triggered immediately. There is a delay up to 1 minute (which is not expected).
I want repeating alarm to trigger immediately without any delay.
Could someone please help me with this?

You should change your code to :
PendingIntent pi=PendingIntent.getBroadcast(context,0,myIntent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(), pi);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(), pi);
}
#Edit
Above code will work for set exactly time. But this section will explain about repeating for alarm manager.
For api below 19, we use AlarmManager.setRepeating() will make alarms trigger exactly at specified time periodically. But from 19 and newer, this method won't work again and there aren't any apis support this behavior. I think this api change make developers thinking more carefully when they create a timer. Because a timer trigger at exactly time periodically will drain battery so much.
If you want you must do on your own. Firstly, you set AlarmManager.setExact() and when alarm trigger, you will make alarm trigger again next time manually
Here is the code:
PendingIntent pi=PendingIntent.getBroadcast(context,0,myIntent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
alarmManager. setExact(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(), pi);
} else {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(), pi);
}
And in your intent, where you put handle code, you should check if android api >= 19, application will create new alarm for the next event.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
doSomething();
// calculate time for next event
Calendar nextEvent = calcNextEvent();
// and set alarm again
alarmManager. setExact(AlarmManager.RTC_WAKEUP, nextEvent.getTimeInMillis(), pi);
} else {
doSomething();
}
I think this is problem in android api design. Old code should work on newer version. Anyway, this new api design make everything clearer for developer, better for system(save battery). Of course, when you use new api :)
Hope this help :)

Related

Android AlarmManager is not triggering alarm on next day when idle

I know there are dozens of similar threads on SO about this topic but I just couldn't find one that really solves the problem / or identifies the root cause.
First of all, I'm targetting SDK 22 (Android 5.1) which means I could use the AlarmManager + WakefulBroadcastReceiver + IntentService even if this is not the latest way of doing things.
I'm not interested in the JobScheduler etc solutions, I just want to understand what is happening and why.
The phone I'm testing on has Android 8.0, but it shouldn't matter as I'm targeting Android 5.1.
So the code I'm dealing with sets the alarm for the next day, 06:00.
private fun setupAlarm() {
val calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis()
calendar.add(Calendar.DAY_OF_YEAR, 1)
calendar.set(Calendar.HOUR_OF_DAY, 6)
calendar.set(Calendar.MINUTE, 0)
val alarmIntent = Intent(this, AlarmReceiver::class.java)
val alarmPendingIntent = PendingIntent.getBroadcast(this, 1221, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, AlarmManager.INTERVAL_DAY, alarmPendingIntent)
}
The AlarmReciever only starts a service:
class AlarmReceiver : WakefulBroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
startWakefulService(context, Intent(context, DownloadingIntentService::class.java));
}
}
This Service then tries to download a file, when finished it calls the completeWakefulIntent(intent) method letting know the system that it's done with its job.
I could not figure out when it is working and when it is not. One morning it did what it should have, on the other, it didn't.
I set up a remote LogCat feature to see whether the IntentService is started but so far I can't see any logs from it, so it means that the alarm is not triggered.
If I set up an alarm for the next minute, even repeating one whatever it works like it should. But when I set back the time for tomorrow morning then it's very unreliable.
Thanks for your help.
I've faced this exact issue myself. See what happens is that setRepeating method let's the android system adjust the time when the alarm should get fired. It will most likely try to batch different alarms in order to optimise battery usage. But in regular cases, if the phone isn't dozing... It generally fires the alarm at correct times.
However if the phone has been idle for a time, the phone goes into doze mode and due to this the alarm gets delayed. I have personally observed delays of upto 1 1:30 hours.
If you want it to fire exactly, you'll have to use the setExactAndAllowWhileIdle method or setAlarmClock method. In this case, you will have to handle the scheduling of your next alarm on your own. The methods work well with doze mode and do fire the alarms at exact times.
There are cons to these methods too. The setExactAndAllowWhileIdle method can only be used to schedule alarms Max once per nine minutes or so. The setAlarmClock method will mostly show a notification like a regular alarm to the user and will indicate the details of the alarm ( this behaviour varies with different os versions )
I used this code to trigger a backup every day. It is working for me, Give it a try.
AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyReceiver.class);
intent.setAction("CUSTOM_INTENT");
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 06);
calendar.set(Calendar.MINUTE, 00);
// setRepeating() lets you specify a precise custom interval--in this case,
// 1 day
alarmMgr.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis()/1000,
AlarmManager.INTERVAL_DAY, alarmIntent);
Try this:
Calendar now = Calendar.getInstance();
Calendar alarm = Calendar.getInstance();
alarm.set(Calendar.HOUR_OF_DAY, hourOfDay);
alarm.set(Calendar.MINUTE, minute);
long alarmMillis = alarm.getTimeInMillis();
if (alarm.before(now)) alarmMillis+= 86400000L; //It will add 1 day if your time selected before now
//set alarm method of yours\\
settingAlarmManager(requestCode, alarmMillis);
private void settingAlarmManager(String requestCode, Calendar calendar) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(AddTaskActivity.this, AlarmReceiver.class);
PendingIntent broadcast = PendingIntent.getBroadcast(AddTaskActivity.this,
Integer.valueOf(requestCode), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, broadcast);
}
My Receiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//do your stuff here\\
}
}
Manifest:
<receiver android:name=".utils.AlarmReceiver" />
I have tried above code for setting alarm and do my custom task. Maybe this will help you.
A few things:
The further in time an Alarm is scheduled, the less precise it will be.
Although you are targeting API Level 22, deprecated elements in higher Android version may not fully work, which is the case of the WakefulBroadcastReceiver
You are trying to run a background job in Android 8.0. It's worth exploring the Foreground Service:
It can be started from the background
Correctly notify users that you are indeed doing something while the phone should be idle.
Do not fear running tasks that might take a few seconds to complete.
You might have killed your application. When a user manually kills an app, in most devices all Alarm's and PendingIntent's are killed as well.
A scheduling strategy many developer use is not to set a repeating Alarm, yet have two single Alarms that reschedule each other continuously ( #Kushan mentioned something similar in his answer).
In short:
Have a Scheduler start as soon as possible during the day (even when a user opens your app, it can be fired multiple times). Check if the desired PendingIntents already exist (your background jobs). If they do not, just schedule them. As well, schedule another Alarm around 11.55.
All this midnight scheduler has to do, is to re-schedule the main AlarmManager in 5 minutes, which is then going to schedule the jobs and the midnight alarm for the next days.
This method allows you to:
schedule exact alarms, since repeating ones do not have the exact option.
reduce the time distance of your scheduled alarms, which will then generally be treated with more precision by the OS.
avoid alarms that trigger immediately because scheduled in the past
Also, try to get the most from the API version you are using:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
rtcStartingTime.getTimeInMillis(),
pendingIntent
);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
this.alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
rtcStartingTime.getTimeInMillis(),
pendingIntent
);
} else {
this.alarmManager.set(
AlarmManager.RTC_WAKEUP,
rtcStartingTime.getTimeInMillis(),
pendingIntent
);
}

Daily alarm not triggered after 2 or 3 days

I want to trigger a daily alarm in my application. I can see the alarm being triggered daily at the correct time for 2 or 3 days but it does not trigger after that. For example if I set alarm to trigger at 08:00 AM, it will trigger at 8 AM daily for 2 or 3 days and after that there is no alarm triggered. There is no app crashing or anything, it simply does not trigger. I have a BroadcastReceiver registered (in AndroidManifest.xml) for this alarm and i can see logs being printed daily at the correct time but only for 2 or 3 days. After that there is no activity and the app just seems to die down.
Please find my code below :
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final long intervalDay = 60*60*24*1000L;
final long alarmTime = calendar.getTimeInMillis();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, alarmTime, intervalDay, pendingIntent);
I have also used alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, alarmTime, AlarmManager.INTERVAL_DAY, pendingIntent); but it did not make any difference (didnt expect it to make any though).
I do not want to use alarmManager.setInexactRepeating() as it does not trigger the alarm at exact time but there is slight delay.
Any help appreciated !!
Thanks.
That's because android cannot hold the alarm content for more than 2-3 days. Though there is no sure shot solution that i know.
I fixed it by cancelling and resetting alarm everytime alarm is triggered.
Something like this.
Instead of using setRepeat use set or setExact to trigger for once:
if(android.os.Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) {
alarmManager.set(AlarmManager.RTC_WAKEUP, alarmtime, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, alarmtime, pendingIntent);
}
Then in onRecieve() method of your alarmReciever after performing your task, reset the alarm again for next followed by cancelling all pending intents:
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), reminderModal.get_remindID(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
pendingIntent.cancel();
Hope it helps.

AlarmManager not working as expected in sleep mode on S5 Neo

I am using an AlarmManager in a Service to be triggered every minute.
PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 0,
getUpdateServiceIntent(mContext), PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
// Cancel any pending Intent
am.cancel(pendingIntent);
// Set a new one
am.set(AlarmManager.RTC_WAKEUP, 60000, pendingIntent);
On the Samsung S5 Neo :
When the screen is active, it is working as expected.
When the screen is off, it is triggered every 5 minutes (instead of one).
I try this exact same code on S5 Mini (with Android 4.4), Nexus 5 5.1 and Nexus 5 6.0, this code is working fine.
targetSdkVersion is 19.
Any idea how to keep the AlarmManager working correctly when screen is off ?
The delay is still 5 minutes, even if I ask for 30 seconds.
EDIT :
I also tried the 'setExact' method, but it didn't change anything. Still have a 5 minutes interval between each alarm.
You should probably use
AlarmManager#setExact(int type, long triggerAtMillis, PendingIntent operation)
instead of
AlarmManager#set(int type, long triggerAtMillis, PendingIntent operation))
Take a look
From google :
AlarmManager#set(int type, long triggerAtMillis, PendingIntent operation))
Note: Beginning in API 19, the trigger time passed to this method is treated as inexact: the alarm will not be delivered before this time, but may be deferred and delivered some time later. The OS will use this policy in order to "batch" alarms together across the entire system, minimizing the number of times the device needs to "wake up" and minimizing battery use. In general, alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.
Edit :
What i am using for an alarm application :
Manifest :
<uses-permission android:name="android.permission.WAKE_LOCK"/>
Java Code :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(nextAlarm.getTimeInMillis(), pendingIntent);
alarmManager.setAlarmClock(alarmClockInfo, pendingIntent);
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(android.app.AlarmManager.RTC_WAKEUP, nextAlarm.getTimeInMillis(), pendingIntent);
}else {
alarmManager.set(android.app.AlarmManager.RTC_WAKEUP, nextAlarm.getTimeInMillis(), pendingIntent);
}

Android programming use of alarmmanger

I want to show toast at a specific time using AlarmManger but my toast is not shown at given time? Help me.
My code is as follows:
private void startAlarm() {
Calendar cal=Calendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH,9);
cal.set(Calendar.MONTH,7);
cal.set(Calendar.YEAR,2015);
cal.set(Calendar.HOUR_OF_DAY,2);
cal.set(Calendar.MINUTE,55);
cal.set(Calendar.AM_PM,Calendar.PM);
Intent intent = new Intent(this, WelcomActivity.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 0, intent, 0);
AlarmManager alarmManager = (AlarmManager)getApplicationContext().getSystemService(ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
Toast.makeText(this, "Alarm worked.", Toast.LENGTH_LONG).show();
}
The documentation for setInexactRepeating states:
Your alarm's first trigger will not be before the requested time, but it might not occur for almost a full interval after that time.
[edit thanks to ci_]
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.
So you need to use one-time exact alarms and reschedule as described in the documentation.

Alarm manager not triggering alarms at exact time in android

I scheduled alarm using Calendar class as below
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY,1);
cal.getTimeInMillis();
cal.set(Calendar.MINUTE,05);
long TriggerMillis = cal.getTimeInMillis();
AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
aManager.set(AlarmManager.RTC_WAKEUP, TriggerMillis,pIntent);
where pIntent is an pending intent to proceed further when alarm triggers.
The event triggers with few seconds delay. Is that any problem using Calendar class for this task. Any suggestions?
TIA..
You have two issues:
It is not reliable to use a _WAKEUP alarm with a service directly. The only reliable patterns involve WakefulBroadcastReceiver, my WakefulIntentService, or something along those lines, where the PendingIntent will be to a BroadcastReceiver.
If your android:targetSdkVersion is 19 or higher, and you are running on an API Level 19+ device, set() is inexact. Ideally, you allow it to be inexact, or perhaps use setWindow() to control how off it will be, to minimize the power hit of your alarm event. If it absolutely has to occur at a precise moment, you will need to use setExact(). Since setWindow() and setExact() are new to API Level 19, you will need to fall back to set() on older devices, by examining Build.VERSION.SDK_INT and branching accordingly.
I think you should use the function
setExactAndAllowWhileIdle
For example:
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis() + 100, pendingIntent); // +100 is to set nearly to the current time
You have to know that this is not a repeating alarm, so at the end of your BroadcastReceiver you should set the next alarm.
Regards

Categories

Resources