Skip to content
davthecoder
Android Background Work in 2026: Replacing IntentService and JobIntentService
tech

Android Background Work in 2026: Replacing IntentService and JobIntentService

By David Cruz Anaya, Independent Senior Mobile Engineer (Android, Kotlin Multiplatform, Rust)

Updated 11 July 2026
AndroidMobile DevelopmentKotlin
Share:

Android background work: replacing IntentService and JobIntentService

If you landed here from an old tutorial, the short version is this: do not reach for IntentService or JobIntentService in 2026. Both are deprecated. For almost all deferrable background work on Android today, the answer is WorkManager. If the work has to run right now and keep going after the user leaves the app, use a foreground service. Everything else usually belongs in a coroutine.

I first wrote this post in 2020 recommending JobIntentService, and that advice has aged out. So I have rewritten it for how Android background work actually looks now, including a step-by-step migration off IntentService.

Why IntentService and JobIntentService are both deprecated

IntentService shipped back in API 3. It gave you a simple way to run work off the main thread: you handed it an Intent, it queued the work, ran onHandleIntent on a background thread, and stopped itself when the queue drained. For years it was the default choice, and it was genuinely convenient.

Then Android Oreo (API 26) introduced background execution limits to stop apps draining the battery with unbounded background services. A plain background service can no longer start freely while your app is in the background, which quietly broke the IntentService model. Google marked IntentService deprecated in API 30 (Android 11).

The first thing most people reached for was JobIntentService: it behaved like IntentService on older devices and used JobScheduler on API 26 and above. It was a useful bridge, and it is exactly what I recommended in the original version of this post. But JobIntentService was itself deprecated in androidx.core 1.3.0. It was always meant to be a migration aid, not a destination.

So both classes are dead ends for new code, and WorkManager is where that work belongs now.

What should you use instead in 2026?

Pick based on what the work actually needs:

  • Deferrable, guaranteed work (uploads, syncs, sending logs, processing a download): use WorkManager. It selects the right scheduler under the hood, survives app restarts and reboots, honours constraints like “only on unmetered network” or “only while charging”, and works back to API 14. This covers most of what people used IntentService for.
  • Work that must run immediately and continue while the app is backgrounded (an active upload with a progress notification, audio, location): use a foreground service, or WorkManager expedited work with a foreground notification.
  • Simple async work tied to a screen (a network call for the current UI): you probably do not need a service at all. Use a Kotlin coroutine in your viewModelScope.

For the classic IntentService case, WorkManager is the direct replacement.

How to migrate IntentService to WorkManager

Here is the migration on a real example: an IntentService that uploads a file.

  1. Add the WorkManager dependency in your module build.gradle.kts. Check for the latest stable version when you add it.
implementation("androidx.work:work-runtime-ktx:2.10.0")
  1. Turn the IntentService into a CoroutineWorker. The old onHandleIntent body becomes doWork. Because doWork is a suspend function, you get structured concurrency for free and never touch a thread by hand.
class UploadWorker(
    context: Context,
    params: WorkerParameters,
) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        val fileUri = inputData.getString(KEY_FILE_URI) ?: return Result.failure()
        return try {
            uploadFile(fileUri)
            Result.success()
        } catch (e: IOException) {
            Result.retry()
        }
    }

    companion object {
        const val KEY_FILE_URI = "file_uri"
    }
}
  1. Replace context.startService(intent) with an enqueued WorkRequest. Whatever you used to pack into the Intent extras now goes into inputData.
val request = OneTimeWorkRequestBuilder<UploadWorker>()
    .setInputData(workDataOf(UploadWorker.KEY_FILE_URI to fileUri))
    .build()

WorkManager.getInstance(context).enqueue(request)
  1. Delete the <service> entry from AndroidManifest.xml. WorkManager registers its own component through the app-startup provider, so the old service tag and the BIND_JOB_SERVICE permission both go away. One less thing to maintain.

That is the whole migration. You drop the manual thread management, the manifest boilerplate, and the API-level branching, and you pick up retries, constraints, and reboot persistence.

If you need the work to start as soon as possible rather than when the system feels like it, mark it expedited:

val request = OneTimeWorkRequestBuilder<UploadWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

Frequently Asked Questions

Is IntentService removed, or just deprecated? It is deprecated, not removed, so old code still compiles and runs. But it is subject to the background execution limits from API 26, so it will not reliably start from the background on modern devices. Treat it as something to migrate away from, not something to keep building on.

Do I still need JobIntentService for devices below API 26? No. WorkManager already supports API 14 and up and chooses the right scheduler internally, so there is nothing left for JobIntentService to solve. Backwards compatibility was its one reason to exist, and WorkManager covers it.

Can WorkManager run work immediately? By default WorkManager optimises for battery, so work can be delayed. If you need it to start right away, use expedited work with setExpedited, or use a foreground service for work that must run continuously while the user can see it.

What replaces onHandleIntent? doWork inside a Worker or CoroutineWorker. With CoroutineWorker it is a suspend function, so you write normal sequential Kotlin and let structured concurrency handle threading and cancellation for you.


Happy coding.

David Cruz davthecoder.com

Share:

Comments

Loading comments…