AlarmManager use setExactAndAllowWhileIdle so it repeats - android

I have an app that uses an AlarmManager to schedule a service to repeat at a set frequency, say every hour.
From Android 6, devices will drop into Doze mode, this mode ignores wakelock and Alarms, potentially running them in the maintenance window. I would prefer that the Alarm executes when it is scheduled even if the device is in low-power idle modes.
I understand i can use setExactAndAllowWhileIdle to execute the alarm in Doze but this will excute only once. I cannot see any method that has this functionality but will repeat at a set frequency.
for example i use the code below to fire an alarm every hour. Is there a way to use setExactAndAllowWhileIdle so it repeats?
// get a Calendar object with current time
Calendar cal2 = Calendar.getInstance();
// add 5 minutes to the calendar object
cal2.add(Calendar.MINUTE, 1);
Intent intentTracking = new Intent(getApplicationContext(), TrackingAlarmReceiver.class);
// In reality, you would want to have a static variable for the request code instead of 192837
PendingIntent sender3 = PendingIntent.getBroadcast(getApplicationContext(), 192839, intentTracking, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am3 = (AlarmManager) getSystemService(ALARM_SERVICE);
//am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
am3.setRepeating(AlarmManager.RTC_WAKEUP, cal2.getTimeInMillis(), ((Integer.parseInt(carerTrackingInteval)) * 60000 ), sender3);

ADM is spot-on.
As part of the work in TrackingAlarmReceiver, you call setExactAndAllowWhileIdle() to schedule the next bit of work.
Bear in mind that the minimum granularity of such events is ~10 minutes IIRC, and that you may not have network access even though you get control.

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

What is the difference between setExact, setInexactRepeating and setRepeating? when and which to use?

Hi I am using alarmManager to trigger alarm for every 3 minutes. So I used setRepeating method for alarm to trigger for every 3 minutes. This works in some device. But when it goes in sleep mode and then turned on, alarm is not working.
public void startAt3() {
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
/* Set the alarm */
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
/* Repeating on every 3 minute interval */
manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 3, pendingIntent);
}
I read that it goes to doze mode in android 6.0. So which is appropriate to use instead of setRepeating method?
setExact - This is used to set an alarm to be delivered precisely at the stated time.
setInexactRepeating - This is used to set some predefined time interval for the alarm. Is this better than setRepeating()? This approach consumes less resource, less battery in comparison with the other. So it is recommended to use setInexactRepeating() whenever possible. Android will synchronize multiple in-exact alarms at run time to save resources. Below are the possible values for in-exact repeating; (prior to api level 19)
INTERVAL_DAY
INTERVAL_HALF_DAY
INTERVAL_HOUR
INTERVAL_HALF_HOUR
INTERVAL_FIFTEEN_MINUTES
setRepeating - This is used to set exact time for the alarm down to milliseconds. Do we really have to use this? Since this method is more resource consuming, Nobody recommend this approach unless it is really necessary.

How to use CPU to perform any operation in Deep Sleep mode

I'm new in android. I struggle with my application approximately 3 weeks. I need sent and receive packets in normal mode and sleep mode. My app must exchange data a 5 seconds. I tried using alarmmanager but on android 5 it's not works. On android 5 an interval changes it on 60 seconds. Such a solution makes the battery wears out quickly. When I use normal asynctask, not IntentService, then it works only when screen is ON and app is visible. When app is hidden or I click power OFF then exchange data stops working. What is the best solutions?
Even RTC_WAKEUP doesn't help most of the times.
Solution that worked for my app when device in deep sleep mode:
Use WakefulBroadcastReceiver combined with AlarmManager.
Service is started by startWakefulService() and when it is finished, it releases the wake lock by calling completeWakefulIntent(intent). So the device will be allowed to go back to sleep.
I'm not adding any code. Search for examples on how to use WakefulBroadcastReceiver with AlarmManager. Even WakefulBroadcastReceiver doc has some template code.
Also reduce the frequency of alarm so you can avoid draining so much battery.
You can use the AlarmManager class to wake up the device at a particular time, then fire off an operation at whatever interval you'd like. Code from the docs found here:
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 20, alarmIntent);
Notice the last line of this block. You can use the method setRepeating() to set whatever interval you'd like.

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

Alarm SET OFF AND ON in Android

I am creating a clock application that has alarm feature too. The time is showing up properly and I am also setting multiple alarm properly.
I am creating multiple alarm using different id and also saving the same into Database so that I can view the list of alarms in a listview. Now I am trying to set ON and OFF functionality for my alarm. I have a problem there.
On itemclick if alarm is ON it switches OFF with the help of:
Intent intent = new Intent(Main.this,TaskRecieverForAlarm.class);
PendingIntent pi = PendingIntent.getBroadcast(Main.this, Integer.parseInt(cont[0]), intent, 0);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.cancel(pi);
The above code cancels the alarms perfectly fine.
To switch ON the alarm I am using:
Intent intent = new Intent(Main.this, TaskRecieverForAlarm.class);
intent.putExtra("AlarmDate", cont[1]);
intent.putExtra("key", Integer.parseInt(cont[0]));
PendingIntent sender = PendingIntent.getBroadcast(Main.this, Integer.parseInt(cont[0]) , intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
if(type.equalsIgnoreCase("daily"))
{
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 1440*60000 ,sender);
}
else if(type.equalsIgnoreCase("weekly"))
{
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender); am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 7*1440*60000 ,sender);
}
Now as soon as I click the OFF to ON, alarm triggers and calls the TASKReceiverFORAlarm (broadcast receiver) even though the alarm time is 4 or 5 hours from the current time. I am not sure where I am going wrong?
Can somebody help me out?
Thanks!
I think I found the answer here:
public void setRepeating (int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
Added in API level 1
Schedule a repeating alarm. Note: for timing operations (ticks, timeouts, etc) it is easier and much more efficient to use Handler. If there is already an alarm scheduled for the same IntentSender, it will first be canceled.
Like set(int, long, PendingIntent), except you can also supply a rate at which the alarm will repeat. This alarm continues repeating until explicitly removed with cancel(PendingIntent). If the time occurs 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.
If an alarm is delayed (by system sleep, for example, for non _WAKEUP alarm types), a skipped repeat will be delivered as soon as possible. After that, future alarms will be delivered according to the original schedule; they do not drift over time. For example, if you have set a recurring alarm for the top of every hour but the phone was asleep from 7:45 until 8:45, an alarm will be sent as soon as the phone awakens, then the next alarm will be sent at 9:00.
If your application wants to allow the delivery times to drift in order to guarantee that at least a certain time interval always elapses between alarms, then the approach to take is to use one-time alarms, scheduling the next one yourself when handling each alarm delivery.
Parameters
type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or RTC_WAKEUP.
triggerAtMillis time in milliseconds that the alarm should first go off, using the appropriate clock (depending on the alarm type).
intervalMillis interval in milliseconds between subsequent repeats of the alarm.
operation Action to perform when the alarm goes off; typically comes from IntentSender.getBroadcast().
The way you use that function is:
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 1440*60000 ,sender);
Try this one:
//The only variable here is the desired hour of the alarm, which
// has to be obtained in milliseconds
long alarmSetAt = // The hour of the Alarm for the current date in milliseconds
long time = cal.getTimeInMillis() - alarmSetAt;
if(time > 0){
time = -time + cal.getTimeInMillis();
}
else{
time = time + cal.getTimeInMillis() + 1440*60000;
}
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 1440*60000 ,sender);

Categories

Resources