Category Archives: Multiple Alarms

Using IntentService With AlarmManager to Schedule Alarms

Many Linux developers look for a cron like service when starting to develop on Android, well luckily it exists in the form of AlarmManager. AlarmManager allows you to schedule your application to run at a time in the future, however it is cleared on boot–should/can be re registered with the system through OnBoot BroadcastReceiver.

AndroidManifest.xml; you need to register the two BroadcastReceivers, and the service. Also get permission to use the WakeLock, and to get Boot_Completed signal.

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
     <receiver android:name=".service.OnBootReceiver" >
         <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
         </intent-filter>
     </receiver>

     <receiver android:name=".service.OnAlarmReceiver" >
     </receiver>

     <service android:name=".service.TaskButlerService" >
     </service>
</application>

This is a BroadcastReceiver for the OnBoot complete, used to reschedule alarms with the AlarmManager since after boot the alarms are flashed out. There is only 2 lines of code in the onReceive() method, that is due to your own onReceive() needing to be short. The first line acquires a partial WakeLock to keep the CPU running, while our IntentService is executing.

package edu.worcester.cs499summer2012.service;

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

/**
 * BroadCastReceiver for android.intent.action.BOOT_COMPLETED
 * passes all responsibility to TaskButlerService.
 * @author Dhimitraq Jorgji
 *
 */
public class OnBootReceiver extends BroadcastReceiver{
	@Override
	public void onReceive(Context context, Intent intent) {

		WakefulIntentService.acquireStaticLock(context); //acquire a partial WakeLock
		context.startService(new Intent(context, TaskButlerService.class)); //start TaskButlerService
	}
}

IntentService is my favorite way of getting things done in the background, separate from the main thread of my application. Usually I don’t inherit IntentService directly, and I suggest you do the same; define a synchronized method to acquire a WakeLock before you continue on with whatever you need to accomplish.

package edu.worcester.cs499summer2012.service;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;

/**
 * Acquires a partial WakeLock, allows TaskButtlerService to keep the CPU alive
 * until the work is done.
 * @author Dhimitraq Jorgji
 *
 */
public class WakefulIntentService extends IntentService {
	public static final String
	LOCK_NAME_STATIC="edu.worcester.cs499summer2012.TaskButlerService.Static";;
	public static final String
	LOCK_NAME_LOCAL="edu.worcester.cs499summer2012.TaskButlerService.Local";
	private static PowerManager.WakeLock lockStatic=null;
	private PowerManager.WakeLock lockLocal=null;

	public WakefulIntentService(String name) {
		super(name);
	}
	/**
	 * Acquire a partial static WakeLock, you need too call this within the class
	 * that calls startService()
	 * @param context
	 */
	public static void acquireStaticLock(Context context) {
		getLock(context).acquire();
	}

	synchronized private static PowerManager.WakeLock getLock(Context context) {
		if (lockStatic==null) {
			PowerManager
			mgr=(PowerManager)context.getSystemService(Context.POWER_SERVICE);
			lockStatic=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
					LOCK_NAME_STATIC);
			lockStatic.setReferenceCounted(true);
		}
		return(lockStatic);
	}

	@Override
	public void onCreate() {
		super.onCreate();
		PowerManager mgr=(PowerManager)getSystemService(Context.POWER_SERVICE);
		lockLocal=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
				LOCK_NAME_LOCAL);
		lockLocal.setReferenceCounted(true);
	}

	@Override
	public void onStart(Intent intent, final int startId) {
		lockLocal.acquire();
		super.onStart(intent, startId);
		getLock(this).release();
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		lockLocal.release();
	}
}

Now we can simply inheriting the WakeFulIntentService, and do all our work with in one simple method onHandleIntent(Intent). The method can be called from within anywhere in your program and it will handle everything on a background thread like any Service, also safely since it holds a WakeLock until the method completes at which point it returns the lock and exits nicely.

package edu.worcester.cs499summer2012.service;

import java.util.List;

import android.content.Intent;

import edu.worcester.cs499summer2012.database.TasksDataSource;
import edu.worcester.cs499summer2012.task.Task;

/**
 * An IntentService that takes care of setting up alarms for Task Butler
 * to remind the user of upcoming events
 * @author Dhimitraq Jorgji
 *
 */
public class TaskButlerService extends WakefulIntentService{

	public TaskButlerService() {
		super("TaskButlerService");
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		TasksDataSource db = TasksDataSource.getInstance(this); //get access to the instance of TasksDataSource
		TaskAlarm alarm = new TaskAlarm();

		List<Task> tasks = db.getAllTasks(); //Get a list of all the tasks there
		for (Task task : tasks) {
			// Cancel existing alarm
			alarm.cancelAlarm(this, task.getID());

			//Procrastinator and Reminder alarm
			if(task.isPastDue()){
				alarm.setReminder(this, task.getID());
			}

			//handle repeat alarms
			if(task.isRepeating() && task.isCompleted()){
				task = alarm.setRepeatingAlarm(this, task.getID());
			}

			//regular alarms
			if(!task.isCompleted() && (task.getDateDue() >= System.currentTimeMillis())){
				alarm.setAlarm(this, task);
			}
		}
		super.onHandleIntent(intent);
	}
}

At this point you just need a BroadcastReceiver to receive your alarms.

package edu.worcester.cs499summer2012.service;

import edu.worcester.cs499summer2012.database.TasksDataSource;
import edu.worcester.cs499summer2012.task.Task;

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

/**
 * BroadCastReceiver for Alarms, displays notifications as it receives alarm
 * and then starts TaskButlerService to update alarm schedule with AlarmManager
 * @author Dhimitraq Jorgji
 *
 */
public class OnAlarmReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		WakefulIntentService.acquireStaticLock(context); //acquire a partial WakeLock

		//send notification, bundle intent with taskID
		NotificationHelper notification = new NotificationHelper();
		Bundle bundle = intent.getExtras();
		int id = bundle.getInt(Task.EXTRA_TASK_ID);
		TasksDataSource db = TasksDataSource.getInstance(context);
		Task task = db.getTask(id);

		if(task.hasFinalDateDue() || task.getPriority() == Task.URGENT){
			notification.sendPersistentNotification(context, task); // send basic notification
		} else {
			notification.sendBasicNotification(context, task); // send basic notification
		}

		context.startService(new Intent(context, TaskButlerService.class)); //start TaskButlerService
	}
}

The setAlarm method:
NOTE:I use a method to create PendingIntent so my pending intents mach if the id passed in is the same, and because of of the FLAG_UPDATE_CURRENT the pending intent updates a possibly existing PendingIntent rather than duplicating.

	/**
	 * Set a One Time Alarm using the taskID
	 * @param context
	 * @param id id of task to retrieve task from SQLite database
	 */
	public void setAlarm(Context context, int id){
		TasksDataSource db = TasksDataSource.getInstance(context);
		Task task = db.getTask(id);
		AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
		am.set(AlarmManager.RTC_WAKEUP, task.getDateDue(), getPendingIntent(context, id));
	}

	//get a PendingIntent
	PendingIntent getPendingIntent(Context context, int id) {
		Intent intent =  new Intent(context, OnAlarmReceiver.class)
		.putExtra(Task.EXTRA_TASK_ID, id);
		return PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
	}

All of the source code for Task Butler can be found on GitHub, including the above code.

Any questions shoot away in the comments bellow.

From the blog Live and Code » WSU CS by dhimitraq and used with permission of the author. All other rights reserved by the author.