For deferrable, guaranteed background work (syncing data periodically, uploading a file even if the app is closed), WorkManager is the recommended API — it respects battery optimization and device constraints automatically, unlike a raw Service or Thread.

class SyncMoviesWorker(context: Context, params: WorkerParameters) :
    CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return try {
            val repository = /* obtain via Hilt WorkerFactory */ TODO()
            repository.syncMovies()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

val syncRequest = PeriodicWorkRequestBuilder<SyncMoviesWorker>(
    repeatInterval = 6, TimeUnit.HOURS
).setConstraints(
    Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
).build()

WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "sync_movies", ExistingPeriodicWorkPolicy.KEEP, syncRequest
)

Result.retry() triggers WorkManager’s built-in backoff policy automatically — you don’t need to implement retry timing yourself. enqueueUniquePeriodicWork with KEEP prevents duplicate scheduling if this code runs more than once (e.g., on every app launch).