Android set() and setExact() alarms firing at incorrect intervals - java

I'm developing an app that should perform a certain task every 60 seconds. Since there's some accuracy problems with alarms in Android 4.4+, where all alarms are inexact, I've opted for the chained model: A BroadcastReceiver fires the first alarm, and each alarm in turn sets the next alarm.
The problem is that, even though I'm setting the alarms at intervals of 60 seconds (60000 ms), the alarms trigger at 5 second intervals, and sometimes even less. I've tested the code on my Nexus 5 (Android 5.1.1) and on an Android 5.0.1 emulator, both giving the same result.
I should point out that both receivers are registered on the AndroidManifest and my application has the RECEIVE_BOOT_COMPLETED permission.
EDIT: setExact() causes exactly the same problem
StartupReceiver.java (BroadcastReceiver for BOOT_COMPLETED):
public class StartupReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Got the BOOT_COMPLETED signal");
// Get the first alarm to be invoked immediately
AlarmReceiver.setNextScanAlarm(context, 0);
}
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Start the service
Intent startServiceIntent = new Intent(context, BackgroundService.class);
startServiceIntent.putExtra("interval", 60000);
startServiceIntent.putExtra("action", "scan");
context.startService(startServiceIntent);
// Schedule the next alarm
setNextScanAlarm(context, 60000);
}
public static void setNextScanAlarm(Context context, int interval) {
Intent scanIntent = new Intent(context, AlarmReceiver.class);
scanIntent.putExtra("interval", interval);
scanIntent.putExtra("action", "scan");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
scanIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
interval,
pendingIntent);
}
}
What could be the problem?

I believe because this is an alarm clock when calling
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
interval,
pendingIntent);
The variable you are calling interval is the amount of time you want to elapse UNTIL the next alarm , but when you think about this when does it know to start? More so, when does time actually equal zero? When you create it? No. When you call .set()? No. It is actually zero upon BOOT. So you are asking it to launch 60 seconds after boot, and your asking for this everytime, this time will have already elapsed.
This is where the confusion is, and where you should probably just use a call like new
Handler.postDelayed(Runnnable r, 60000) instead of an alarm Manager. It will be much more accurate and will not be subject to some problems with understanding the Android Operating System and its alarms/clocks/etc/etc.
But for your specific case I believe you could solve it by accessing System function calls/variables. So inside of your function setNextScanAlarm() I believe it would look like this:
public static void setNextScanAlarm(Context context, int interval) {
//create the intent the same way as before
Intent scanIntent = new Intent(context, AlarmReceiver.class);
scanIntent.putExtra("interval", interval);
scanIntent.putExtra("action", "scan");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
scanIntent,
PendingIntent.FLAG_ONE_SHOT);
//create new variables to calculate the correct time for this to go off
long timeRightNow = System.elapsedRealTime() //use something else if you change AlarmManager type
long timeWhenIShouldGoOff = timeRightNow + interval;
//use the new timeWhenIShouldGoOff variable instead of interval
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
timeWhenIShouldGoOff,
pendingIntent);
}

See my answer to a similar question.
I use postDelayed() instead of AlarmManager for the short time intervals (less than 1 minute), and AlarmManager for long.

Related

Android Alarm repeats only once using AlarmManager

I am using AlarmManager to create an alarm that is supposed to be repeated every x seconds. The alarm indeed fires onces, then again after the desired period of time but then never again.
Here is my code
MyActivity.java
// Retrieve a PendingIntent that will perform a broadcast
Intent alarmIntent = new Intent(this, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
int interval = 3000; // 3 sec
manager.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
interval,
pendingIntent);
MyReceiver.java
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context.getApplicationContext(), "I'm running", Toast.LENGTH_SHORT).show();
}
}
When I run my application I see a Toast saying "I'm running" and then 5 (not 3 for some reason) seconds later another toast. After that I see nothing else.
How can I make my alarm to keep repeating until I stop it?

AlarmManager not calling BroadcastReceiver precisely every interval

I have the following BroadcastReceiver:
public class LocationUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
LocationHelper locationHelper = new LocationHelper(context);
locationHelper.updateLocation();
//.. do stuff with location
}
public static void SetAlarm(Context context, Long time){
Intent intentAlarm = new Intent(context, LocationUpdateReceiver.class);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getBroadcast(context, 1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), time, pi);
}
public static void CancelAlarm(Context context) {
Intent intent = new Intent(context, LocationUpdateReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
}
I call the setAlarm method like this to test from an Activity:
LocationUpdateReceiver.SetAlarm(this, 3000L);
You will notice I call setRepeating() which I thought was supposed to call the receiver class exactly every 3 seconds?
The trouble is that more often then not it doesn't call the receiver for over 5 minutes, this is quite a large deviation. I intend to run the receiver every 30 minutes, but should I expect a comparative percentage in deviation time?
How can I make sure that it will get called exactly (or with very small deviation) to the interval I specify?
From the docs of setRepeating():
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.
You can use setExact() and set the intervals yourself.

Setting an alarm from inside another alarm using AlarmManager class not getting fired correctly

The below code is from my MainActivity onCreate() method where I set an alarm to run a service ScheduleAlarm immediately and only once. sPrefs is the SharedPreference object that takes care of setting this alarm only once. The service ScheduleAlarm is an IntentService which is getting fired perfectly.
if(sPrefs.getBoolean("SettingAlarmForFirstTime",true)) {
//Creating alarm for showing notifications.
Calendar calendar = Calendar.getInstance();
//calendar.setTimeInMillis(System.currentTimeMillis());
//calendar.set(Calendar.HOUR_OF_DAY, 8);
Intent intent = new Intent(MainActivity.this, ScheduleAlarms.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);//Use AlarmManager.INTERVAL_DAY instead of int number here.
editor.putBoolean("SettingAlarmForFirstTime", false);
editor.commit();
}
Now this is my ScheduleAlarms service class. When this service is started onHandleIntent() method is called from where I am calling setWeeklyAlarms() method. Now my problem is from inside this method I want to set 7 alarms for a complete week based on timings that I will obtain from an API server call. Right now I am unable to even execute a single alarm perfectly. The alarm that i am setting is set to start after 3 secs delay which will call another service named NotificationService but the alarms gets fired immediately instead of waiting for 3 secs. Please analyze and tell me why this is happening.
public class ScheduleAlarms extends IntentService {
private boolean notificationsEnabled = true;
private String notificationTimings = "";
public ScheduleAlarms() {
super("ScheduleAlarms");
}
#Override
protected void onHandleIntent(Intent intent) {
setWeeklyAlarm();
}
private void setWeeklyAlarm() {
Intent i = new Intent(ScheduleAlarms.this, NotificationService.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getService(ScheduleAlarms.this, 10001, i, 0);
AlarmManager alarmManager = (AlarmManager) ScheduleAlarms.this.getSystemService(Context.ALARM_SERVICE);
/* Setting this alarmManager to fire up the alarm after 3 mins delay, but this alarm is getting fired immediately*/
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3*60*1000, pendingIntent);
}
}
Change the following line:
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3*60*1000, pendingIntent);
to:
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, c.getTimeInMillis() + 3 * 60 * 1000, 3 * 60 * 1000, pendingIntent);
The alarm is fired up immediately, because
AlarmManager.ELAPSED_REALTIME_WAKEUP + 0, ...
So, the first time, it is fired up after 0 seconds. To change this, you need to add some time here.
AlarmManager.ELAPSED_REALTIME_WAKEUP + time
And, also it should be repeating, so you need to use setRepeating.
Note : 3 * 60 * 1000 == 3 minutes, not 3 seconds

Android AlarmManager firing at random times

I'm developing an application which has to show some Notifications and that has to download some data two times a day, so i created a Service for launching my Notifications and a BroadcastReceiver which should (depending on the time) run my NotificationService and later also my DownloadService.
My problem is now, that the AlarmManager-alarm i created should call fire every hour (not important if 1 or 2 minutes less or more), and it does that for about 3 to 4 hours but then it runs randomly and also multiple times in between for example 7 and 8 o'clock.
I have no idea where the problem could be so here is my code:
Activity:
Intent myIntent = new Intent(OverviewActivity.this, Receiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(OverviewActivity.this, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, notifCal.getTimeInMillis(), AlarmManager.INTERVAL_HOUR, pendingIntent);
Receiver:
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent arg1) {
Intent notificationService = new Intent(context, NotificationService.class);
context.startService(notificationService);<br/>
}
}
Thanks in advance,
momob114
Each time your Activity is fired your app is cancelling and recreating the Alarm which could be the cause of the seemingly random calls (which could actually correspond to the time you opened the app, or one hour later).
Note in case you still want to do that, instead of cancelling the previous alarm you can just call :
PendingIntent pendingIntent = PendingIntent.getBroadcast(OverviewActivity.this, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Keep also in mind that a reboot of the device will clear any alarms that you might have set. So you'll probably want to create a BroadcastReceiver listening for the android.intent.action.BOOT_COMPLETED action in order to set your repeating alarm then.

Android: How to use AlarmManager

I need to trigger a block of code after 20 minutes from the AlarmManager being set.
Can someone show me sample code on how to use an AlarmManager in ِAndroid?
I have been playing around with some code for a few days and it just won't work.
"Some sample code" is not that easy when it comes to AlarmManager.
Here is a snippet showing the setup of AlarmManager:
AlarmManager mgr=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i=new Intent(context, OnAlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), PERIOD, pi);
In this example, I am using setRepeating(). If you want a one-shot alarm, you would just use set(). Be sure to give the time for the alarm to start in the same time base as you use in the initial parameter to set(). In my example above, I am using AlarmManager.ELAPSED_REALTIME_WAKEUP, so my time base is SystemClock.elapsedRealtime().
Here is a larger sample project showing this technique.
There are some good examples in the android sample code
.\android-sdk\samples\android-10\ApiDemos\src\com\example\android\apis\app
The ones to check out are:
AlarmController.java
OneShotAlarm.java
First of, you need a receiver, something that can listen to your alarm when it is triggered. Add the following to your AndroidManifest.xml file
<receiver android:name=".MyAlarmReceiver" />
Then, create the following class
public class MyAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Alarm went off", Toast.LENGTH_SHORT).show();
}
}
Then, to trigger an alarm, use the following (for instance in your main activity):
AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, MyAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, 30);
alarmMgr.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(), pendingIntent);
.
Or, better yet, make a class that handles it all and use it like this
Bundle bundle = new Bundle();
// add extras here..
MyAlarm alarm = new MyAlarm(this, bundle, 30);
this way, you have it all in one place (don't forget to edit the AndroidManifest.xml)
public class MyAlarm extends BroadcastReceiver {
private final String REMINDER_BUNDLE = "MyReminderBundle";
// this constructor is called by the alarm manager.
public MyAlarm(){ }
// you can use this constructor to create the alarm.
// Just pass in the main activity as the context,
// any extras you'd like to get later when triggered
// and the timeout
public MyAlarm(Context context, Bundle extras, int timeoutInSeconds){
AlarmManager alarmMgr =
(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyAlarm.class);
intent.putExtra(REMINDER_BUNDLE, extras);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, timeoutInSeconds);
alarmMgr.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(),
pendingIntent);
}
#Override
public void onReceive(Context context, Intent intent) {
// here you can get the extras you passed in when creating the alarm
//intent.getBundleExtra(REMINDER_BUNDLE));
Toast.makeText(context, "Alarm went off", Toast.LENGTH_SHORT).show();
}
}
What you need to do is first create the intent you need to schedule. Then obtain the pendingIntent of that intent. You can schedule activities, services and broadcasts. To schedule an activity e.g MyActivity:
Intent i = new Intent(getApplicationContext(), MyActivity.class);
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),3333,i,
PendingIntent.FLAG_CANCEL_CURRENT);
Give this pendingIntent to alarmManager:
//getting current time and add 5 seconds in it
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
//registering our pending intent with alarmmanager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), pi);
Now MyActivity will be launched after 5 seconds of the application launch, no matter you stop your application or device went in sleep state (due to RTC_WAKEUP option).
You can read complete example code Scheduling activities, services and broadcasts #Android
I wanted to comment but <50 rep, so here goes. Friendly reminder that if you're running on 5.1 or above and you use an interval of less than a minute, this happens:
Suspiciously short interval 5000 millis; expanding to 60 seconds
See here.
Some sample code when you want to call a service from the Alarmmanager:
PendingIntent pi;
AlarmManager mgr;
mgr = (AlarmManager)ctx.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(DataCollectionActivity.this, HUJIDataCollectionService.class);
pi = PendingIntent.getService(DataCollectionActivity.this, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() , 1000, pi);
You dont have to ask userpermissions.
An AlarmManager is used to trigger some code at a specific time.
To start an Alarm Manager you need to first get the instance from the System. Then pass the PendingIntent which would get executed at a future time that you specify
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(context, MyAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
int interval = 8000; //repeat interval
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
You need to be careful while using the Alarm Manager.
Normally, an alarm manager cannot repeat before a minute. Also in low power mode, the duration can increase to up to 15 minutes.

Categories

Resources