Alarms

Intro

If you have a task that you want to run at a specific date and time, even when your application is not running, then you may consider using an Alarm.  For example, an alarm can be used to trigger a Service with a long-running background task, or a task that needs to run at a given time interval.  Alarms can be set to run just once, or at a regularly scheduled time.  You can also specify whether the alarm should wake the device  or not.

Creating an Alarm

There are four steps to creating an alarm:

1.  Get an instance of the system AlarmManager:  

AlarmManager alarmManager = (AlarmManager) getBaseContext()
    .getSystemService(Context.ALARM_SERVICE);

2.  Create an Intent for the action you want to occur when the Alarm is triggered.  The action in this case is a broadcast receiver that is capable of receiving the alarm:

Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);

3.  Pass the Intent as a parameter of a new PendingIntent item.  A PendingIntent allows another application to run predefined code using your app's permissions.  In this case, the other application is the AlarmManager:

PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, intent, 0);

4.  Set the alarm to fire at a specific time.  For example, to set the alarm to fire in exactly (1) minute from now, you would use this line:

alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000, pendingIntent);

Alarm Broadcast Receiver

The AlarmReceiver class used in the example above (see item #2) is a custom class that extends the BroadcastReceiver class and overrides the onReceive method.  This allows us to specify what actions to take when the alarm broadcast message is received:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class AlarmReceiver extends BroadcastReceiver {
	
    @Override
    public void onReceive(Context context, Intent intent) {
        // do something here
    }	
}

Like Activities and Services, the receiver needs to have an entry on the application's manifest file:

<receiver
    android:name="edu.usna.cs.alarm.AlarmReceiver"
/>

Cancelling the Alarm

Alarms can be cancelled at any time like so:

if (alarmManager!= null) {
    alarmManager.cancel(pendingIntent);
}

Types of Alarms

In the above example, the alarm is triggered once at a specific number of milliseconds from the current time, and wakes the device if needed.  There are actually several other types of alarms.  From Google:

Alarms can be fired once, or set to repeat at specified intervals.  You should choose the proper alarm type for your particular situation and desired behavior.  For specific examples of how to implement each type, see here:  http://developer.android.com/training/scheduling/alarms.html#type

Reset On Reboot

All alarms are cancelled when a device is rebooted.  Any alarm that you want/need to persist after a reboot must be rescheduled at boot time.  In order to do this, your app must be able to detect when a reboot occurs.  As opposed to rewriting the wheel, refer to this excellent write-up from Google (with examples!) on how to accomplish this: http://developer.android.com/training/scheduling/alarms.html#boot 

Demonstration

Let's put it all together by creating a sample application.  In our sample app, there is a Main activity with two buttons: one button to set the Alarm and one to cancel it.  Once set, the alarm will trigger in 10 seconds unless cancelled.  Once it fires, the alarm broadcast is received by our AlarmReceiver class, which in turn runs a custom IntentService.  While simple, this app could easily be extended to fit nearly any situation.

MainActivity.java

package edu.usna.cs.alarm;

import android.os.Bundle;
import android.os.SystemClock;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {
	
    long alarmSeconds = 10;
	
    AlarmManager alarmManager;
    Intent intent;
    PendingIntent pendingIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
        alarmManager = (AlarmManager)getBaseContext().getSystemService(Context.ALARM_SERVICE);
	intent = new Intent(getBaseContext(), AlarmReceiver.class);
	pendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, intent, 0);
    }

    public void setAlarm(View view){
	alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
            SystemClock.elapsedRealtime() + alarmSeconds * 1000, pendingIntent);
	Log.i("PEPIN", "Alarm set for " +  alarmSeconds);
    }
	
    public void cancelAlarm(View view){
	if (alarmManager!= null) {
	    alarmManager.cancel(pendingIntent);
	}
	Log.i("PEPIN", "Alarm cancelled");
    }
}

AlarmReceiver.java

package edu.usna.cs.alarm;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class AlarmReceiver extends BroadcastReceiver {
	
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("PEPIN", "Alarm triggered, starting service!!");
			
	Intent serviceIntent = new Intent(context,
	    ServiceReceivingAlarm.class);
	    context.startService(serviceIntent);
    }
	
}

ServiceReceivingAlarm.java

package edu.usna.cs.alarm;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

public class ServiceReceivingAlarm extends IntentService {
	
    public ServiceReceivingAlarm() {
	super("ServiceRecievingAlarm");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
	Log.i("PEPIN", "Service started by alarm!!");
    }

}

AlarmExample Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="edu.usna.cs.alarm"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="edu.usna.cs.alarm.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name="edu.usna.cs.alarm.ServiceReceivingAlarm"
            android:exported="false" >
        </service>
        
        <receiver
            android:name="edu.usna.cs.alarm.AlarmReceiver"
            android:process=":remote" />
        
    </application>

</manifest>

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Set Alarm"
        android:onClick="setAlarm" />
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Cancel Alarm"
        android:onClick="cancelAlarm" />

</LinearLayout>