Managing Background Tasks on Android using the Alarm Manager

November 27th, 2011 by

In today’s tutorial we’re going to take a look on how to handle periodically scheduled tasks on our Android device by using BroadcastReceivers, Services and the AlarmManager.

 

Prerequisites

The following environment is needed to follow this tutorial …

The Concept

We’re going to create a first broadcast receiver that gets initiated at boot time. This component then should make use of Android’s AlarmManager API and schedule a cyclic task using an explicit intent.

A second broadcast receiver handles this intent and starts the final service.

The first Receiver

The following receiver instanciates the AlarmManager and instructs him to send off an intent every 20 seconds.

package com.hascode.android;
 
import java.util.Calendar;
 
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
 
public class SchedulerSetupReceiver extends BroadcastReceiver {
	private static final String APP_TAG = "com.hascode.android";
 
	private static final int EXEC_INTERVAL = 20 * 1000;
 
	@Override
	public void onReceive(final Context ctx, final Intent intent) {
		Log.d(APP_TAG, "SchedulerSetupReceiver.onReceive() called");
		AlarmManager alarmManager = (AlarmManager) ctx
				.getSystemService(Context.ALARM_SERVICE);
		Intent i = new Intent(ctx, SchedulerEventReceiver.class); // explicit
																	// intent
		PendingIntent intentExecuted = PendingIntent.getBroadcast(ctx, 0, i,
				PendingIntent.FLAG_CANCEL_CURRENT);
		Calendar now = Calendar.getInstance();
		now.add(Calendar.SECOND, 20);
		alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
				now.getTimeInMillis(), EXEC_INTERVAL, intentExecuted);
	}
 
}

This is the receiver’s declaration in our AndroidManifest.xml. Please note that we’re filtering for two intents: action.BOOT_COMPLETED and action.USER_PRESET and we’re forcing the receiver to run in a specific process: “:hascode_process. The following receiver and services are also running in this process.

<receiver android:name="SchedulerSetupReceiver" android:process=":hascode_process">
    <intent-filter >
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.USER_PRESENT" />
    </intent-filter>
</receiver>

Catching the Intent

Now we’re creating a second broadcast receiver to catch our explicit intent fired from the AlarmManager.

This class does not do much here – it’s just starting the final class – our service

package com.hascode.android;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
 
public class SchedulerEventReceiver extends BroadcastReceiver {
	private static final String APP_TAG = "com.hascode.android";
 
	@Override
	public void onReceive(final Context ctx, final Intent intent) {
		Log.d(APP_TAG, "SchedulerEventReceiver.onReceive() called");
		Intent eventService = new Intent(ctx, SchedulerEventService.class);
		ctx.startService(eventService);
	}
 
}

That’s how the receiver is declared in our AndroidManifest.xml

<receiver android:name="SchedulerEventReceiver" android:process=":hascode_process"/>

Creating the Service

And last but not least the service is executed. There is only one specialty here .. we’re using onStartCommand instead of onStart and we’re returning Service.START_NOT_STICKY. First of all – onStart is deprecated in Android 2.0 and has no return value.

The return value of onStartCommand allows us to specify what to do when our Service is killed. In our case, START_NOT_STICKY tells the operating system that it may forget about the service if it has to kill it .. in our scenario this is not really a problem because the next tick from the AlarmManager will be creating a new instance (indirectly) anyway …

package com.hascode.android;
 
import java.util.Date;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
 
public class SchedulerEventService extends Service {
	private static final String APP_TAG = "com.hascode.android.scheduler";
 
	@Override
	public IBinder onBind(final Intent intent) {
		return null;
	}
 
	@Override
	public int onStartCommand(final Intent intent, final int flags,
			final int startId) {
		Log.d(APP_TAG, "event received in service: " + new Date().toString());
		return Service.START_NOT_STICKY;
	}
 
}

And the service’s declaration in the manifest

<service android:name="SchedulerEventService" android:process=":hascode_process"/>

Just to be sure – here is my complete AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hascode.android"
    android:versionCode="1"
    android:versionName="1.0" xmlns:android="http://schemas.android.com/apk/res/android">
 
    <uses-sdk android:minSdkVersion="8" />
 
    <application
        android:debuggable="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
 
        <service android:name="SchedulerEventService" android:process=":hascode_process">
        </service>
 
        <receiver android:name="SchedulerEventReceiver" android:process=":hascode_process">
        </receiver>
        <receiver android:name="SchedulerSetupReceiver" android:process=":hascode_process">
            <intent-filter >
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>
    </application>
 
</manifest>

Running the Service

If you’re running the project on your device or using the emulator you should be able to see an output similar to this using logcat or DDMS

Event output in DDMS perspective

Event output in DDMS perspective


Tutorial Sources

I have put the source from this tutorial on my Bitbucket repository – download it there or check it out using Mercurial:

hg clone https://bitbucket.org/hascode/android-scheduling-tutorial

Resources

package com.hascode.android;

import java.util.Calendar;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class SchedulerSetupReceiver extends BroadcastReceiver {
private static final String APP_TAG = “com.hascode.android”;

private static final int EXEC_INTERVAL = 20 * 1000;

@Override
public void onReceive(final Context ctx, final Intent intent) {
Log.d(APP_TAG, “SchedulerSetupReceiver.onReceive() called”);
AlarmManager alarmManager = (AlarmManager) ctx
.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctx, SchedulerEventReceiver.class); // explicit
// intent
PendingIntent intentExecuted = PendingIntent.getBroadcast(ctx, 0, i,
PendingIntent.FLAG_CANCEL_CURRENT);
Calendar now = Calendar.getInstance();
now.add(Calendar.SECOND, 20);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
now.getTimeInMillis(), EXEC_INTERVAL, intentExecuted);
}

}

Tags: , , , ,

11 Responses to “Managing Background Tasks on Android using the Alarm Manager”

  1. Etienne Says:

    Thank you!

    By far the best tutorial on Android services!

  2. Neil Says:

    Please change these colours, black/white its killing my eyes, good tut tho.

  3. micha kops Says:

    thanks – I could have used white on yellow or purple on red ! ;)

  4. Aritz Says:

    Great tutorial, thank you very much!

  5. Machee Says:

    A big thanks for the codes. Working fine.
    I just want to ask why does the Alarm Manager time, (Exec Interval) doesn’t run properly.
    It calls EventReceiver every 10 seconds. And I can’t change it.
    Any help there would be a very big help.. Thanks in advance. :D

  6. serge Says:

    that’s strange .. did you set EXEC_INTERVAL correctly? on what target platform are you running the application?

  7. Alejandro Says:

    I try to run this project, but nothing happens. Which is the condition to run?

    Thanks a lot.

  8. micha kops Says:

    you should be able to see some log output using logcat or in your DDMS perspective – this does not work in your environment?

  9. Sebastian Says:

    How do i start it from my main thread? is it only necessary to start the first receiver from my main activity?

  10. mayowa Says:

    I tried the tutorial above but not working. I don’t see any logs on my log cat. Do i need a set permissions to allow it receive boot complete or what other permissions do i need to set?

  11. Ranjani Says:

    Thanks for the tutorial. I downloaded and ran the code. But do not see any log messages in the logcat. I changed the android target version to 19. Can you please help?

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 81,008 bad guys.

Search
Categories