-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Repeating Periodic Tasks
Repeating periodic tasks within an application is a common requirement. This functionality can be used for polling new data from the network, running manual animations, or simply updating the UI. There are several ways to run periodic tasks:
-
Handler - Execute a
Runnabletask on the UIThread after an optional delay. In-process only — the schedule ends when the app's process does, so use it for short-lived repetition while the app is on screen. - ScheduledThreadPoolExecutor - Execute periodic tasks with a background thread pool. Also in-process only.
- WorkManager - The recommended API for deferrable periodic background work that must survive process death or device reboot (data sync, log uploads, etc.). See the WorkManager guide.
- AlarmManager - Use for exact-time alarms (calendar reminders, alarm clocks) where firing at a precise wall-clock time matters. For deferrable periodic background work, prefer WorkManager.
- TimerTask - Doesn't run in UIThread and is not reliable. Consensus is to never use TimerTask
Recommended methods are outlined below.
We can use a Handler to run code on a given thread after a delay or repeat tasks periodically on a thread. This is done by constructing a Handler and then "posting" Runnable code to the event message queue on the thread to be processed.
Because the posted callbacks live on the process's message queue, a Handler schedule is in-process and short-lived by nature: it stops when the process is killed and is never restored after a reboot. Use it for while-on-screen repetition such as UI updates, manual animations, or foreground polling; for periodic work that must outlive the process, use the WorkManager section below.
Note: The no-argument Handler() constructor is deprecated since API 30 (Android 11) because it implicitly picks up the current thread's Looper, which can lead to subtle bugs. New code should pass the Looper explicitly, e.g. new Handler(Looper.getMainLooper()) to post to the main thread, or use a java.util.concurrent.Executor. The examples below use the legacy constructor; update them accordingly for modern projects. See the Handler reference.
Using a Handler, we can execute arbitrary code a single time after a specified delay:
// We need to use this Handler package
import android.os.Handler;
// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
@Override
public void run() {
// Do something here on the main thread
Log.d("Handlers", "Called on main thread");
}
};
// Run the above code block on the main thread after 2 seconds
handler.postDelayed(runnableCode, 2000);Using a similar technique, we can also use a handler to execute a periodic runnable task as demonstrated below:
// We need to use this Handler package
import android.os.Handler;
// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
@Override
public void run() {
// Do something here on the main thread
Log.d("Handlers", "Called on main thread");
// Repeat this the same runnable code block again another 2 seconds
// 'this' is referencing the Runnable object
handler.postDelayed(this, 2000);
}
};
// Start the initial runnable task by posting through the handler
handler.post(runnableCode);We can remove the scheduled execution of a runnable with:
// Removes pending code execution
handler.removeCallbacks(runnableCode);Note that with a Handler, the Runnable executes in UIThread by default so you can safely update the user interface within the runnable code block. See this handler post and this other handler post for reference.
Refer to our threads and handlers guide for a more advanced breakdown.
A pool of threads which can schedule commands to execute periodically in the background. Useful when multiple worker threads are needed but generally not needed. Like Handler, an executor's schedule is in-process only and does not survive the process being killed. See this guide on how they work or this stackoverflow post.
For deferrable periodic background work that should run reliably even if the app exits or the device restarts (data sync, log uploads, scheduled refreshes), use WorkManager — this is the recommended approach for anything that must outlive the process. Per the official guidance: "the WorkManager API is the recommended replacement for previous Android background scheduling APIs, including FirebaseJobDispatcher and GcmNetworkManager." The system handles persistence across reboots, Doze, and battery-saving constraints automatically.
First, add the WorkManager dependency to your app/build.gradle (2.11.2 is the current stable release — check the WorkManager releases page for the latest):
dependencies {
// WorkManager with Kotlin + coroutines support
implementation "androidx.work:work-runtime-ktx:2.11.2"
}Next, define the task itself as a CoroutineWorker (or a plain Worker for Java) whose doWork() method performs one run of the work and reports the outcome:
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
class SyncDataWorker(appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
return try {
// Do the periodic work here, e.g. poll new data from the network.
// syncData() stands in for your own suspend or blocking function.
syncData()
Result.success()
} catch (e: Exception) {
// Ask WorkManager to retry this run later with backoff
Result.retry()
}
}
}Then schedule it with a PeriodicWorkRequest. Per the official periodic-work guidance: "The minimum repeat interval that can be defined is 15 minutes (same as the JobScheduler API)." Enqueue it as unique work so that re-running this code on every app launch does not stack duplicate schedules:
import java.util.concurrent.TimeUnit
import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
// Call this once at startup, e.g. from Application#onCreate
fun schedulePeriodicSync(context: Context) {
// Only run when the device has a network connection
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
// Repeat roughly once an hour
val syncRequest = PeriodicWorkRequestBuilder<SyncDataWorker>(1, TimeUnit.HOURS)
.setConstraints(constraints)
.build()
// KEEP runs the new request only if no pending work has this unique name
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork("syncData", ExistingPeriodicWorkPolicy.KEEP, syncRequest)
}Two behavioral notes from the same guidance page: the repeat interval "is defined as the minimum time between repetitions" — the exact execution time depends on the constraints in the request and on system optimizations — and if constraints are not met, "the PeriodicWorkRequest will not run until this condition is met," which can delay or even skip a particular run. The schedule can be stopped at any time by unique name:
// context is any Context, as in schedulePeriodicSync above
WorkManager.getInstance(context).cancelUniqueWork("syncData")AlarmManager should be reserved for user-facing exact-time events such as calendar reminders or alarm clocks where firing at a precise wall-clock time matters. For deferrable periodic background work, prefer WorkManager above — the Android docs explicitly recommend AlarmManager only for use cases that require firing at an exact time. See the AlarmManager section of the services guide for details.
- http://www.mopri.de/2010/timertask-bad-do-it-the-android-way-use-a-handler/
- http://stackoverflow.com/questions/18605403/timertask-vs-thread-sleep-vs-handler-postdelayed-most-accurate-to-call-functio
- http://androidtrainningcenter.blogspot.in/2013/12/handler-vs-timer-fixed-period-execution.html
- http://stackoverflow.com/questions/8098806/where-do-i-create-and-use-scheduledthreadpoolexecutor-timertask-or-handler
Created by CodePath with much help from the community. Contributed content licensed under cc-wiki with attribution required. You are free to remix and reuse, as long as you attribute and use a similar license.
Finding these guides helpful?
We need help from the broader community to improve these guides, add new topics and keep the topics up-to-date. See our contribution guidelines here and our topic issues list for great ways to help out.
Check these same guides through our standalone viewer for a better browsing experience and an improved search. Follow us on twitter @codepath for access to more useful Android development resources.