Android Alarmmanager Setexact() Is Not Exact

AlarmManager setExact with WakefulBroadcastReceiver sometimes not exact

This behaviour is added in API 19:

Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.

from AlarmManager.

Important: setExact() still does not have to be exact, as the docs state:

The alarm will be delivered as nearly as possible to the requested trigger time.

Android AlarmManager.setExact() not firing

There are some considerations depending in the Android version in which you are testing the alarm, but if you are testing for Android 6 or later then try the next code:

// Init the Alarm Manager.
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

// Setting the PendingIntent to be fired when alarm triggers.
Intent serviceIntent = new Intent(context.getApplicationContext(), YourService.class);
PendingIntent pendingServiceIntent = PendingIntent.getService(context, 0, serviceIntent, 0);

// Set the alarm for the next seconds.
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + seconds * 1000, pendingServiceIntent);

alarm manager can't start at exact time in android

after lots of researches, I finally found API 26 added a new method for pending intent called getForeGroundService()

so the code should look like this :

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener( view -> startBackup());
}

private void startBackup() {

Intent serviceIntent = new Intent(getApplicationContext(), ExampleService.class);

Calendar cal = initCalendar(10, 0, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

if (Build.VERSION.SDK_INT >= 26) {
PendingIntent pintent = PendingIntent.getForegroundService(getApplicationContext(), 6546548, serviceIntent, 0);
alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pintent);
}else if (Build.VERSION.SDK_INT >= 23) {
PendingIntent pintent = PendingIntent.getService(getApplicationContext(), 6546548, serviceIntent, 0);
alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pintent);
} else if (Build.VERSION.SDK_INT >= 19) {
PendingIntent pintent = PendingIntent.getService(getApplicationContext(), 6546548, serviceIntent, 0);
alarm.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() , pintent);
} else {
PendingIntent pintent = PendingIntent.getService(getApplicationContext(), 6546548, serviceIntent, 0);
alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() , pintent);
}

}

private Calendar initCalendar(int hour, int minute, int second) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);

return calendar;
}
}

AlarmManager.setExact() does not work

The issue is you do not set the mainAlarm for tomorrow on your mainAlarmReciever.

Imagine that your mainAlarmReciever->onRecieve run at 11:54:59 P.M tonight. And it does whatever tasks you want to do. You should also set the mainAlarm for Tomorrow at 11:54:59 P.M in your onRecieve function.

Another option is using setRepeating (as CommonsWare mentioned it is not exact after API 19+).

AlarmManager setExact in Android 5.1

So what do I do if I need to set an alarm for an exact time, even during Doze, in Android 5.1?

Devices running 5.1 do not doze, so you need not worry about that. This

alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent)

will work as expected in 5.1. The newer setExactAndAllowWhileIdle was introduced for use on devices running 6.0+. Your code snippet is ok.

alarms in Android 5.1 (API level 22) or lower do not fire when the system is in Doze

This is a bit of a confusing statement. What they mean to say here is alarms set using the 5.1 or lower API's (like setExact) do not fire when a device is in doze.

Android: Alarm not being triggered through AlarmManager

This part of your code seems wrong:

notificationIntent.setAction("com.example.basicalarmsetter.MatchNotification");

You're using the class name here. You need to use the action of the broadcast receiver, the one you put in your intent filter, a.k.a:

notificationIntent.setAction("com.example.notificationtest.MatchNotification");

Another issue: You're creating two alarms, which is unnecessary, at here:

AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, futureInMillis, pendingIntent);
assert alarmManager != null;
alarmManager.setExact(AlarmManager. ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);

At this section, following lines are unnecessary:

alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, futureInMillis, pendingIntent);
assert alarmManager != null;

The value RTC_WAKEUP is supposed to be used with System.currentTimeMillis(), not SystemClock.elapsedRealtime().

The final of your MainActivity.java would look like this:

package com.example.basicalarmsetter;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;

public class MainActivity extends AppCompatActivity {
private int uniqueId = 0;

// Schedules a notification in the future given the delay
@RequiresApi(api = Build.VERSION_CODES.O)
private void scheduleNotification(int matchId, long delay) {
// Construct the PendingIntent which will trigger our alarm to go off
Intent notificationIntent = new Intent();
notificationIntent.setAction("com.example.notificationtest.MatchNotification");

PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), matchId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT) ;
long futureInMillis = SystemClock.elapsedRealtime() + delay;

// Set off our PendingIntent
AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmManager.setExact(AlarmManager. ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
}

// Sets an Alarm at a future specified date
@RequiresApi(api = Build.VERSION_CODES.O)
private void setAlarm(long notificationDelay) {
try {
System.out.println("Setting alarm at " + notificationDelay + " seconds");

// Sets off a notification after 5 seconds
scheduleNotification(uniqueId, notificationDelay);

uniqueId++;

} catch (Exception ex) {
System.out.println("Cannot print alarm!");
System.out.println("Exception: " + ex.toString());
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

setAlarm(15000);
}
}


Related Topics



Leave a reply



Submit