background-shape

How to implement an alarm in your android application using AlarmManager?

Alarm

Android AlarmManager allows you to access system alarm.

With the help of AlarmManager in android, you can schedule your application to run at a specific time in the future.

AlarmManager is a bridge between application and Android system alarm service. It can send a broadcast to your app (which can be completely terminated by user) at a scheduled time and your app can then perform any task accordingly.

With the help from PendingIntent and Intent, a bundle of information can also be sent together with the system broadcast to your application when alarm goes off.

However, since Android KitKat (API 19) and Android MarshMallow (API 23), Google has added different restrictions on AlarmManager to reduce the battery usage. Alarm no longer goes off exactly at the assigned time by default. System has the ability to defer any alarm in order to optimise the device performance.

Note: This should be keep in mind that AlarmManager doesn’t keep record of the scheduled alarms after the device is booted. Your application should re-register all the scheduled alarms after the device is booted.

An alarm notification can be sent to the application, at the scheduled time when the BroadcastReceiver receives the intent, using Notification.Builder


Now it’s time to understand the code behind it :

Step 1. Create a BroadcastReceiver :

  1. First create a class that extends BroadcastReceiver and implement onReceive() method in the class

    BroadcastReceiver is a class for receiving and handling the broadcast sent to your application. In system alarm case, it receives broadcast from the system alarm service when alarm goes off.

    public class AlarmReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if ((Intent.ACTION_BOOT_COMPLETED).equals(intent.getAction())){
                // reset all alarms
            }
            else{
                // perform your scheduled task here (eg. send alarm notification) 
            }
    
        }
    }
    

    Whenever your device reboots the BroadcastReceiver is called and with the help of Intent we can re-register all the alarms using action ACTION_BOOT_COMPLETED .

    In the else part you can perform your task (e.g, send alarm notification).

    We will further see how to send notification to your application.

  2. Register this receiver in your AndroidManifest.xml file

    <receiver
        android:name=".AlarmReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    

        <action android:name="android.intent.action.BOOT_COMPLETED"/> is added         in <intent-filter> so that we can handle the situation to re-register all the         scheduled alarms when the device will reboot.

Step 2: Setup in activity :

        Code in the activity to schedule an alarm at any specific time is explained step by         step below:

  1. Get AlarmManager instance

    import android.app.AlarmManager;
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    

    AlarmManager is actually a system service and thus can be retrieved from Context.getSystemService() function with parameter Context.ALARM_SERVICE.

  2. Prepare for an intent

    import android.content.Intent;
    Intent intent = new Intent(this, AlarmReceiver.class);
    intent.putExtra("any_data",123);
    

        Android allows a bundle of information to be sent to a target receiver (i.e.             AlarmReceiver, which is defined in step 1) when alarm goes off. The designated         receiver and the corresponding information can be set in Intent by setting its         action and extra. These information can later be retrieved at the onReceive()         callback in the designated BroadcastReceiver and action field is checked to         ensure the correctness of the system broadcast.

  1. Prepare for an PendingIntent

    import android.app.PendingIntent;
    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    

        PendingIntent is a reference pointing to a token maintained by the Android         system. Thus, it will still be valid if the application is killed by user and can be         broadcasted at some moment in the future.

        2nd parameter of getBroadcast() is requestCode which should be unique for         every pendingIntent when you want to schedule multiple alarms.

        For your information:

        There are totally 4 functions for initialising a PendingIntent but only 1 of them is         applicable:

                1. PendingIntent.getBroadcast() — Applicable to AlarmManager

                2. PendingIntent.getActivity()

                3. PendingIntent.getActivities()

                4. PendingIntent.getService()

        Flags indicates how system should handle the new and existing PendingIntent s         that have the same Intent0 indicates that system will use its default way to         handle the creation of PendingIntent. The following are some examples:

                1. FLAG_UPDATE_CURRENT

                2. FLAG_CANCEL_CURRENT

                3. FLAG_IMMUTABLE

  1. Set the alarm time and sent to system

    The best way to set the time of the alarm is by using a Calendar object

    import java.util.Calendar;
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.HOUR_OF_DAY, hourOfDay);  // set hour
    cal.set(Calendar.MINUTE, minute);          // set minute   
    cal.set(Calendar.SECOND, 0);               // set seconds
    
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(),pendingIntent);
    

        The simplest way to set an alarm is using setExactAndAllowWhileIdle() with         parameter RTC_WAKEUP. This would tell Android fires the alarm exactly at the         assigned time no matter the system is in doze mode (idle mode).

        If you want to repeat your alarm on a particular Day of Week then: 

cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); 

long interval = 7 * 24 * 60 * 60 * 1000;   // interval of 7 days
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), interval , pendingIntent); 

        Set the day of week you want repeat the alarm to Calendar.DAY_OF_WEEK and use         setRepeating() method of alarmManager.

        Some more methods of alarmManager :

                1. alarmManager.set()

                2. alarmManager.setExact()

                3. alarmManager.setAlarmClock()

                4. alarmManager.setInExactRepeating()

                5. alarmManager.setRepeating()

                6. alarmManager.setTime()

                7. alarmManager.setTimeZone()     


Canceling of an alarm:

However, it is tricky to cancel a PendingIntent since it depends on both the Intent and requestCode used.

The alarm with PendingIntent can be cancelled with the following steps:

  • Get AlarmManager instance

    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    
  • Prepare for the Intent

    Intent intent = new Intent(this, AlarmReceiver.class);
    
  • Prepare for the PendingIntent

    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0 , intent, PendingIntent.FLAG_UPDATE_CURRENT);
    

        Note : To cancel a particular scheduled alarm the requestCode used in                   PendingIntent must be same to that which is used to register that alarm.

  • Now cancel the alarm using AlarmManager instance

    alarmManager.cancel(pendingIntent);
    

Handling of an alarm event:

AlarmManager provides two ways to listen to an alarm broadcast. They are

  1. Local listener (AlarmManager.OnAlarmListener)

  2. BroadcastReceiver specified at the Intent wrapped inside a PendingIntent.

The implementation of AlarmManager.OnAlarmListener is similar to the one using PendingIntent, instead it requires a callback and its corresponding Handler:

String tag = "TAG";
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), tag, new AlarmManager.OnAlarmListener() {
     @Override
     public void onAlarm() {
         Toast.makeText(OwnerSettingsActivity.this, "AlarmManager.OnAlarmListener", Toast.LENGTH_SHORT).show();  
     }
},null);

There is a limitation on AlarmManager.OnAlarmListener over PendingIntent. It cannot work when the corresponding Activity or Fragment is destroyed since the callback object is released at the same time. However, because PendingIntent is sent to the system alarm service, it can be fired even when the application is killed by user.


Summary

Android provides an alarm service to notify application at any specific time with the corresponding assigned information stored in Intent.

Along with the evolution of Android system throughout the years, Android applies more controls of alarm service to optimise the battery consumption.

setExactAndAllowWhileIdle() together with WAKE_UP type should only be used when the alarm time must be as exact as possible.

App developer should bear in mind to re-register all the alarms again when device is booted since Android does not store any alarms by default.

Hope this article can bring something to you! 😉 See you next time

Thank you for reading 🙌🏼