mittelmania
mittelmania

Reputation: 3571

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

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?

Upvotes: 7

Views: 1719

Answers (2)

Evgeny Ivanov
Evgeny Ivanov

Reputation: 534

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.

Upvotes: 0

napkinsterror
napkinsterror

Reputation: 1965

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

Upvotes: 0

Related Questions