Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
📱 Device Features  ·  Lesson 38

WorkManager, Services & Broadcasts

Which tool for background work?

NeedUse
Work while the screen is open (load data)Coroutines in viewModelScope
Must finish even if the app closes: reminders, uploads, syncWorkManager
User-visible ongoing task: music, navigation, workout trackingForeground Service
Exact clock time (alarm app)AlarmManager (exact alarms)

Setup

TOML + Kotlin DSL
# libs.versions.toml
work = "2.10.0"
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }

// app/build.gradle.kts
implementation(libs.androidx.work.runtime.ktx)

Step 1: Write a Worker

Kotlin – reminder/ReminderWorker.kt
package com.codingeasily.studentdiary.reminder

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.codingeasily.studentdiary.R
import com.codingeasily.studentdiary.data.AppDatabase
import com.codingeasily.studentdiary.data.SettingsStore

/** WorkManager runs this at the reminder time, even if the app was closed. */
class ReminderWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val noteId = inputData.getLong(KEY_NOTE_ID, -1L)
        if (noteId == -1L || !SettingsStore(applicationContext).remindersEnabled) return Result.success()

        val note = AppDatabase.get(applicationContext).noteDao().getById(noteId)
            ?: return Result.success()          // note was deleted
        if (note.isDone) return Result.success()

        NotificationHelper.show(
            applicationContext,
            note.id,
            applicationContext.getString(R.string.reminder_title, note.title),
            note.content.ifBlank { note.subject }
        )
        return Result.success()
    }

    companion object {
        const val KEY_NOTE_ID = "note_id"
    }
}
ReturnMeaning
Result.success()Done
Result.retry()Try again later with backoff
Result.failure()Give up

Step 2: Schedule it

Kotlin – reminder/ReminderScheduler.kt
package com.codingeasily.studentdiary.reminder

import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import java.util.concurrent.TimeUnit

object ReminderScheduler {

    private fun workName(noteId: Long) = "reminder_$noteId"

    /** Schedules (or re-schedules) one reminder for a note. */
    fun schedule(context: Context, noteId: Long, atMillis: Long) {
        val delay = atMillis - System.currentTimeMillis()
        if (delay <= 0) return
        val request = OneTimeWorkRequestBuilder<ReminderWorker>()
            .setInitialDelay(delay, TimeUnit.MILLISECONDS)
            .setInputData(workDataOf(ReminderWorker.KEY_NOTE_ID to noteId))
            .build()
        WorkManager.getInstance(context)
            .enqueueUniqueWork(workName(noteId), ExistingWorkPolicy.REPLACE, request)
    }

    fun cancel(context: Context, noteId: Long) {
        WorkManager.getInstance(context).cancelUniqueWork(workName(noteId))
    }
}

Real example: note reminders

Kotlin
// NoteViewModel.save() – schedule or cancel when a note is saved
val id = repo.save(note)
val due = note.dueAt
if (due != null && !note.isDone) ReminderScheduler.schedule(getApplication(), id, due)
else ReminderScheduler.cancel(getApplication(), id)
AppWorkManagerReminderWorkerNotificationenqueueUniqueWork("reminder_7", delay 3h)user closes the app3 hours later: doWork()load note 7 from Roomshow "⏰ Maths homework"
Unique work: editing the note REPLACEs the old reminder

Periodic work and constraints

Kotlin
class BackupWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
    override suspend fun doWork(): Result = try {
        uploadNotesToServer()
        Result.success()
    } catch (e: IOException) {
        Result.retry()                         // WorkManager tries again later (backoff)
    }
}

val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED)   // Wi-Fi only
    .setRequiresCharging(true)
    .build()

val daily = PeriodicWorkRequestBuilder<BackupWorker>(1, TimeUnit.DAYS)
    .setConstraints(constraints)
    .build()

WorkManager.getInstance(context)
    .enqueueUniquePeriodicWork("daily_backup", ExistingPeriodicWorkPolicy.KEEP, daily)

Observe work status

Kotlin
WorkManager.getInstance(requireContext())
    .getWorkInfosForUniqueWorkLiveData("daily_backup")
    .observe(viewLifecycleOwner) { infos ->
        val state = infos.firstOrNull()?.state
        binding.tvBackup.text = "Backup: ${state ?: "not scheduled"}"   // ENQUEUED, RUNNING, SUCCEEDED…
    }

Android Studio → App Inspection → Background Task Inspector shows all scheduled and running work.

Foreground Service (music, tracking)

XML + Kotlin
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<service
    android:name=".PlayerService"
    android:exported="false"
    android:foregroundServiceType="mediaPlayback" />

// PlayerService.kt
class PlayerService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = NotificationCompat.Builder(this, "player")
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle("Playing: Kotlin lecture 3")
            .build()
        startForeground(1, notification)        // must be called within a few seconds
        // … start playback
        return START_NOT_STICKY
    }
    override fun onBind(intent: Intent?): IBinder? = null
}

// start it
ContextCompat.startForegroundService(this, Intent(this, PlayerService::class.java))

Common mistakes

  • Using a plain background Service for uploads → killed on Android 8+; use WorkManager.
  • Non-unique work for reminders → duplicates after every edit; use enqueueUniqueWork with REPLACE.
  • Passing big objects in input data (10 KB limit) → pass an id and read from Room.
  • Foreground service without foregroundServiceType on Android 14 → crash.

Summary

  • WorkManager = guaranteed background work; CoroutineWorker for suspend code.
  • OneTime with delay for reminders, Periodic (≥15 min) with constraints for sync.
  • Foreground Service only for ongoing, user-visible tasks.
✅ Practice

Schedule a daily "Revision time!" notification at about 7 PM with PeriodicWorkRequest, and show its status (ENQUEUED/RUNNING) on the Settings screen.

Frequently Asked Questions

Does WorkManager run at the exact time?
Not exactly. WorkManager guarantees the work will run, but the system may delay it a little to save battery (Doze). For alarm-clock accuracy use AlarmManager.setExactAndAllowWhileIdle with the exact-alarm permission.
What is the minimum interval for periodic work?
15 minutes. Shorter intervals are rounded up to 15 minutes.
Does work survive a phone restart?
Yes. WorkManager stores scheduled work in its own database and reschedules it after reboot.
← Android Tutorial (Kotlin)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicWebView (Website in App) Your progress: 38 / 43 (88%) · Lesson 39 / 43 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free