Skip to content

Instantly share code, notes, and snippets.

@MohammadSamandari
Last active August 6, 2024 22:44
Show Gist options
  • Save MohammadSamandari/507b092f64b02200dd69f45a04c95a60 to your computer and use it in GitHub Desktop.
Save MohammadSamandari/507b092f64b02200dd69f45a04c95a60 to your computer and use it in GitHub Desktop.
Android Alarm Manager

Alarm Manager

how to use alarms to trigger tasks at specific times, whether or not your app is running when the alarms go off.

For example, you can use a repeating alarm to schedule a download every day at the same time.

To create alarms, you use the AlarmManager class. Alarms in Android have the following characteristics:

  • Alarms let you send an Intent at set times or intervals. You can use alarms with broadcast receivers to start services and perform other operations.
  • Alarms operate outside your app. You can use them to trigger events or actions even when your app isn't running, and even if the device is asleep.
  • When used correctly, alarms can help you minimize your app's resource requirements. For example, you can schedule operations without relying on timers or continuously running background services.

Alarm types

Elapsed real-time alarms

Elapsed real-time alarms use the time, in milliseconds, since the device was booted.

The AlarmManager class provides two types of elapsed real-time alarm:

  • ELAPSED_REALTIME: Fires a PendingIntent based on the amount of time since the device was booted, but doesn't wake the device. The elapsed time includes any time during which the device was asleep. All repeating alarms fire when your device is next awake.
  • ELAPSED_REALTIME_WAKEUP: Fires the PendingIntent after the specified length of time has elapsed since device boot. If the screen is off, this alarm wakes the device's CPU. If your app has a time dependency, for example if your app has a limited window during which to perform an operation, use this alarm instead of ELAPSED_REALTIME.

Real-time clock (RTC) alarms

Real-time clock (RTC) alarms are clock-based alarms that use Coordinated Universal Time (UTC). Only choose an RTC alarm in these types of situations:

  • You need your alarm to fire at a particular time of day.
  • The alarm time is dependent on current locale.

The AlarmManager class provides two types of RTC alarm:

  • RTC: Fires the pending intent at the specified time but doesn't wake the device. All repeating alarms fire when your device is next awake.
  • RTC_WAKEUP: Fires the pending intent at the specified time. If the screen is off, this alarm wakes the device's CPU.

Scheduling an alarm

The AlarmManager class gives you access to the Android system alarm services. AlarmManager lets you broadcast an Intent at a scheduled time, or after a specific interval.

To schedule an alarm:

  1. Call getSystemService(ALARM_SERVICE) to get an instance of the AlarmManager class.
  2. Use one of the set...() methods available in AlarmManager (as described below). Which method you use depends on whether the alarm is elapsed real time, or RTC.
  • All the AlarmManager.set...() methods include a type argument and a PendingIntent argument:
  • Use the type argument to specify the alarm type. Alarms can be elapsed real-time alarms (ELAPSED_REALTIME or ELAPSED_REALTIME_WAKEUP) or real-time clock alarms (RTC or RTC_WAKEUP).
  • A PendingIntent object, which is how you specify which task to perform at the given time.

Scheduling a single-use alarm

To schedule a single alarm, use one of the following methods on the AlarmManager instance:

  • set(): For devices running API 19+, this method schedules a single, inexactly timed alarm, meaning that the system shifts the alarm to minimize wakeups and battery use. For devices running lower API versions, this method schedules an exactly timed alarm.
  • setWindow(): For devices running API 19+, use this method to set a window of time during which the alarm should be triggered.
  • setExact(): For devices running API 19+, this method triggers the alarm at an exact time. Use this method only for alarms that must be delivered at an exact time, for example an alarm clock that rings at a requested time. Exact alarms reduce the OS's ability to minimize battery use, so don't use them unnecessarily.
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, 
             SystemClock.elapsedRealtime() + 1000*300,
             alarmIntent);

Doze and App Standby

API 23+ devices sometimes enter Doze or App Standby mode to save power:

  • Doze mode is triggered when a user leaves a device unplugged and stationary for a period, with the screen off. During short "maintenance windows," the system exits Doze to let apps complete deferred activities, including firing standard alarms, then returns to Doze. Doze mode ends when the user returns to their device.
  • App Standby mode is triggered on idle apps that haven't been used recently. App Standby mode ends when the user returns to your app or plugs in the device.

If you need an alarm that fires during Doze mode or App Standby mode without waiting for a maintenance window:

  • For inexact alarms, use setAndAllowWhileIdle() and for exact alarms, use setExactAndAllowWhileIdle(), or
  • Set a user-visible alarm (API 21 and higher).

Scheduling a repeating alarm

You can also use the AlarmManager to schedule repeating alarms, using one of the following methods:

  • setRepeating(): On devices running Android versions lower than 4.4 (API Level 19), this method creates a repeating, exactly timed alarm. On devices running API 19 and higher, setRepeating() behaves exactly like setInexactRepeating().
  • setInexactRepeating(): This method creates a repeating, inexact alarm that allows for batching. When you use setInexactRepeating(), Android synchronizes repeating alarms from multiple apps and fires them at the same time. This reduces the total number of times the system must wake the device, thus reducing drain on the battery. As of API 19, all repeating alarms are inexact.

Here's an example of using setInexactRepeating() to schedule a repeating alarm:

alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
               calendar.getTimeInMillis(),
               AlarmManager.INTERVAL_FIFTEEN_MINUTES,
               alarmIntent);
  • The first occurrence of the alarm is sent immediately, because calendar.getTimeInMillis() returns the current time as UTC milliseconds.

Checking for an existing alarm

To check for an existing alarm:

  1. Create a PendingIntent that contains the same Intent used to set the alarm, but this time, use the FLAG_NO_CREATE flag.
  • With FLAG_NO_CREATE, a PendingIntent is only created if one with the same Intent exists. Otherwise, the request returns null.
  1. Check whether the PendingIntent is null:
  • If the PendingIntent is null, the alarm has not yet been set. If the PendingIntent is not null, the PendingIntent exists, meaning that the alarm has been set.

For example, the following code returns true if the alarm contained in alarmIntent already exists:

boolean alarmExists = 
 (PendingIntent.getBroadcast(this, 0, 
       alarmIntent,
       PendingIntent.FLAG_NO_CREATE) != null);

Canceling an alarm

To cancel an alarm, use cancel() and pass in the PendingIntent. For example:

alarmManager.cancel(alarmIntent);

User-visible alarms ("alarm clocks")

For API 21+ devices, you can set a user-visible alarm clock by calling setAlarmClock(). Apps can retrieve the next user-visible alarm clock that's set to go off by calling getNextAlarmClock().

Alarm clocks set with setAlarmClock() work even when the device or app is idle, similar to the way setExactAndAllowWhileIdle() works. Using setAlarmClock() gets you as close to an exact-time wake-up call as possible.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mohammad.samandari.standup">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver
android:name=".AlarmReceiver"
android:exported="false"></receiver>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
package mohammad.samandari.standup;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.core.app.NotificationCompat;
public class AlarmReceiver extends BroadcastReceiver {
private NotificationManager mNotificationManager;
private static final int NOTIFICATION_ID = 0;
private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
private static final String TAG = "Lord";
@Override
public void onReceive (Context context, Intent intent) {
Log.d(TAG, "onReceive: AlarmReceiver Class");
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
deliverNotification(context);
}
private void deliverNotification (Context context) {
Log.d(TAG, "deliverNotification: AlarmReceiver Class");
Intent contentIntent = new Intent(context, MainActivity.class);
PendingIntent contentPendingIntent = PendingIntent.getActivity(context, NOTIFICATION_ID, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, PRIMARY_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stand_up)
.setContentTitle("Stand Up Alert")
.setContentText("You should stand up and walk around now!")
.setContentIntent(contentPendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
package mohammad.samandari.standup;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import org.w3c.dom.Text;
import java.time.Clock;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
private NotificationManager mNotificationManager;
ToggleButton alarmToggle;
Intent notifyIntent;
private PendingIntent notifyPendingIntent;
private AlarmManager alarmManager;
private static final int NOTIFICATION_ID = 1;
private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
private static final String TAG = "Lord";
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: Started");
//initialize mNotificationManager using getSystemService().
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
alarmToggle = findViewById(R.id.alarmToggle);
createNotificaionChannel();
notifyIntent = new Intent(this, AlarmReceiver.class);
//To check if the alarm is on or not. and updating the toggle button.
boolean alarmUp = (PendingIntent.getBroadcast(this, NOTIFICATION_ID, notifyIntent,
PendingIntent.FLAG_NO_CREATE) != null);
alarmToggle.setChecked(alarmUp);
notifyPendingIntent = PendingIntent.getBroadcast(this, NOTIFICATION_ID, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged (CompoundButton buttonView, boolean isChecked) {
String toastMassage;
if (isChecked) {
Log.d(TAG, "onCheckedChanged: IsChecked");
toastMassage = "Stand Up Alarm On!";
////If the Toggle is turned on, set the repeating alarm with a 15 minute interval
long repeatInterval = AlarmManager.INTERVAL_HALF_HOUR;
long triggerTime = SystemClock.elapsedRealtime() + repeatInterval;
if (alarmManager != null) {
Log.d(TAG, "onCheckedChanged: AlarmManager!=null - Start of alarm manager");
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, repeatInterval, notifyPendingIntent);
}
} else {
Log.d(TAG, "onCheckedChanged: IsNotChecked");
toastMassage = "Stand Up Alarm Off!";
alarmManager.cancel(notifyPendingIntent);
mNotificationManager.cancelAll();
}
//Show a toast to say the alarm is turned on or off.
Toast.makeText(MainActivity.this, toastMassage, Toast.LENGTH_SHORT).show();
}
});
}
/**
* Creates a Notification channel, for OREO and higher.
*/
private void createNotificaionChannel () {
Log.d(TAG, "createNotificaionChannel: MainActivity");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// Create the NotificationChannel with all the parameters.
NotificationChannel notificationChannel = new NotificationChannel(PRIMARY_CHANNEL_ID, "Stand Up Notification", NotificationManager.IMPORTANCE_DEFAULT);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setDescription("Notifies every 15 minutes to stand up and walk");
mNotificationManager.createNotificationChannel(notificationChannel);
}
}
}
@DorBSoft
Copy link

DorBSoft commented Aug 6, 2024

I think it is a good article. One comment though.
Generally speaking, checking getBroadcast with FLAG_NO_CREATE does not tell if an alarm is set. It tells if such PendingIntent was already created and not cancelled. Regardless of its usage. Though with very careful usage it can indicate that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment