Scheduling Background Tasks in Android

Share this article

Periodically your Android application might need to perform a task, such as checking a server for updates. The AlarmManager class can be used to schedule and execute operations that need to occur even if the application isn’t running.

The AlarmManager class enables the scheduling of repeated alarms that will run at set points in the future. The AlarmManager is given a PendingIntent to fire whenever an alarm is scheduled. When an alarm is triggered, the registered Intent is broadcast by the Android system, starting the target application if it’s not already running.

Alarm Type and Precision

There are two major alarm types, Elapsed Realtime and Real Time Clock. The first corresponds to the time since system boot and the second to UTC time. The different alarm types can be set to either wake up the device’s CPU when in sleep mode, or to fire when the device is next awake. Below is a list of the different alarm type variations available.

  • ELAPSED_REALTIME – Fires the pending intent after the specified length of time since device boot. If the device is asleep, it fires when the device is next awake.

  • ELAPSED_REALTIME_WAKEUP – Fires the pending intent after the specified length of time since device boot. It wakes up the device if it is asleep.

  • RTC – Fires the pending intent at a specified time. If the device is asleep, it will not be delivered until the next time the device wakes up.

  • RTC_WAKEUP – Fires the pending intent at a specified time, waking up the device if asleep.

Deciding on the alarm to use depends on the requirements of the task at hand. Time elapsed is suitable for setting alarms that should fire based on the passage of time, for instance regularly checking a server for changes. If you want to run a task dependent on the current locale, then the real time clock will be more suitable.

Note that elapsed time is usually the better choice. If the user changes their time settings or moves to a new locale, the change might cause some unexpected behaviour in the app. If you set a specific time for an app to sync with a server, the server could be overwhelmed when all instances of the app hit at the same time.

Setting up a recurring task

We’ll create a simple application to show the scheduling of tasks. The task we’ll schedule will be the displaying of a message using Toast (A toast provides simple feedback about an operation in a small popup). Code for this project can be found at this Git Repo.

Create a new Android project. For the view, create two buttons that will start and stop the periodic operations.

In res/layout/activity_main.xml

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

    <Button
        android:id="@+id/button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Start Alarm"
        android:onClick="startAlarm" />

    <Button
        android:id="@+id/button2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Cancel Alarm"
        android:onClick="cancelAlarm" />

</LinearLayout>

To receive intents, we’ll set up a broadcast receiver to perform an operation when the alarm is fired. Create a class that inherits from BroadcastReceiver. In the onReceive method, which is called when the BroadcastReceiver is receiving an Intent broadcast, we will set up the code that runs our task.

In AlarmReceiver.java

package com.example.alarmexample;

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

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context arg0, Intent arg1) {
        // For our recurring task, we'll just display a message
        Toast.makeText(arg0, "I'm running", Toast.LENGTH_SHORT).show();

    }

}

We then need to register the BroadcastReceiver in the manifest file. Declare the AlarmReceiver in the manifest file.

<application>
    .
    .
    <receiver android:name=".AlarmReceiver"></receiver>
    .
    .
</application>

To start the alarm we’ll set up the method startAlarm declared in res/layout/activity_main.xml above as the method to run when the Start Alarm button is clicked.

In MainActivity.java declare a PendingIntent and an AlarmManager variable. The PendingIntent will be used to set and cancel the alarms.

In MainActivity.java include the following instance variables.

private PendingIntent pendingIntent;
private AlarmManager manager;

In onCreate() we create an Intent that references our broadcast receiver class and uses it in our PendingIntent.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Retrieve a PendingIntent that will perform a broadcast
    Intent alarmIntent = new Intent(this, AlarmReceiver.class);
    pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
}

We then include the method that will set up the recurring alarms. Once set, the alarm will fire every 10 seconds.

public void startAlarm(View view) {
    manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
    int interval = 10000;

    manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
    Toast.makeText(this, "Alarm Set", Toast.LENGTH_SHORT).show();
}

Run the application and by clicking the “Start Alarm” button an “Alarm Set” message will appear, followed by the message “I’m running” every 10 seconds.

We used the setRepeating() method to set up a recurring alarm, but setInexactRepeating() could also be used. With setInexactRepeating() repeating alarms from multiple applications will be syncronized and fired at the same time. This will reduce the number of times the device is woken up, saving battery power. As of Android 4.4, all repeating alarms are inexact.

The setRepeating() method takes four arguments:

  1. type – alarm type, specified by the units of time to use and whether or not it should occur when the device is in sleep mode. Can be ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC, or RTC_WAKEUP.

  2. triggerAtMillis – time in milliseconds that the alarm should first fire.

  3. intervalMillis – interval in milliseconds between subsequent repeats of the alarm.

  4. operation – Action to perform when the alarm fires

Next, we’ll set up the cancelAlarm() method to stop the alarms. Include the method below in MainActivity.java

public void cancelAlarm(View view) {
    if (manager != null) {
        manager.cancel(pendingIntent);
        Toast.makeText(this, "Alarm Canceled", Toast.LENGTH_SHORT).show();
    }
}

Before running the application again, Force Stop or uninstall the app in your device or simulator. If you don’t, the previous alarm schedule will still be running. Run the application and the scheduled alarm can be stopped.

The above is a simple example showing how to use the AlarmManager to set up and cancel alarms. I use the same AlarmManager object to start and stop the alarm. This of course will not persist when the app is exited, so for a real app instead of only canceling the alarm when manager is not null (which it will be if the app was exited, no matter if the alarm is running or not), you should cancel the alarm with an equivalent PendingIntent to the one used setting it. For example you could do this instead.


Intent alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
manager.cancel(pendingIntent);
Toast.makeText(this, "Alarm Canceled", Toast.LENGTH_SHORT).show();

Conclusion

We have looked at how the AlarmManager can be used to schedule periodic tasks. The AlarmManager is best used for tasks that need to run even when the application isn’t open. For normal timing operations that need to run during application use (ticks, timeouts, etc), it is more efficient to use the postDelayed() and postAtTime() methods of a Handler. The AlarmManager requires too much overhead to justify it being used this way.

Frequently Asked Questions (FAQs) about Scheduling Background Tasks in Android

What is the importance of scheduling background tasks in Android?

Scheduling background tasks in Android is crucial for optimizing the performance of your application. It allows you to perform tasks at optimal times, reducing the load on system resources and improving the overall user experience. For instance, you can schedule non-urgent tasks like data syncing or updates during times when the device is idle or connected to Wi-Fi. This not only ensures smooth operation of your app but also conserves battery life and data usage.

How does WorkManager help in scheduling background tasks?

WorkManager is a powerful Android library that simplifies the process of scheduling background tasks. It takes into account various factors like device API level, Doze mode, and App Standby mode to choose the most suitable way to run your background tasks. WorkManager also supports periodic tasks and tasks with complex dependency chains. It ensures that your tasks will be executed even if the app or device restarts.

What is the difference between AlarmManager and JobScheduler for scheduling tasks?

AlarmManager and JobScheduler are both Android components used for scheduling tasks, but they have different capabilities and use cases. AlarmManager is a simpler tool that can schedule tasks to be executed at a specific time. However, it doesn’t consider system constraints like network availability or battery level. On the other hand, JobScheduler, available from Android 5.0 (API level 21), allows you to set conditions for your tasks, like network availability or charging status. However, it’s more complex to use than AlarmManager.

How can I handle tasks that need to run at precise times?

For tasks that need to run at precise times, you can use AlarmManager in combination with a BroadcastReceiver. AlarmManager can trigger an Intent at a specific time, and the BroadcastReceiver can then handle this Intent to execute your task. However, keep in mind that from Android 6.0 (API level 23), the system introduces Doze mode and App Standby, which can affect the execution of your alarms.

How can I schedule a periodic task using WorkManager?

To schedule a periodic task using WorkManager, you can use the PeriodicWorkRequest class. You need to specify the worker class that will perform the task and the repeat interval. Here’s a simple example:

PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(MyWorker.class, 24, TimeUnit.HOURS)
.build();
WorkManager.getInstance().enqueue(periodicWork);

In this example, MyWorker is the worker class that performs the task, and the task is scheduled to run every 24 hours.

How can I test my scheduled tasks?

Testing scheduled tasks can be challenging due to the asynchronous nature of these tasks. However, Android provides the WorkManager TestHelper class that allows you to test your Workers and WorkRequests. You can use this class to create a test WorkManager and to set the initial state of your Workers.

How can I debug my scheduled tasks?

Debugging scheduled tasks can be done using Android Studio’s debugging tools. You can set breakpoints in your Worker classes and inspect the state of your tasks when they are executed. Additionally, you can use the adb shell dumpsys jobscheduler command to view the state of your JobScheduler jobs.

Can I cancel a scheduled task?

Yes, you can cancel a scheduled task using the WorkManager’s cancelWorkById() or cancelAllWork() methods. You need to provide the ID of the work you want to cancel, which you can get from the WorkRequest object when you create the task.

How can I handle tasks with network requirements?

If your task requires network connectivity, you can use the setRequiredNetworkType() method of the WorkRequest.Builder class to specify the network type. WorkManager will then ensure that your task is only run when the specified network type is available.

Can I schedule tasks that depend on each other?

Yes, WorkManager supports tasks with complex dependency chains. You can use the then() method of the WorkContinuation class to specify that a task should only run after another task has completed. You can also use the OneTimeWorkRequest and PeriodicWorkRequest classes to create tasks that run once or periodically, respectively.

Joyce EchessaJoyce Echessa
View Author

I am a web developer who dabbles in mobile development from time to time. You can find me on Twitter @joyceechessa to see what I’m up to.

androidbackground processingMobile Developmentscheduling
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week